Search code examples
c++template-meta-programmingboost-hana

boost::hana: Why can't I filter a set?


I'm using boost::hana, and I'd like to filter a boost::hana::set.

#include <boost/hana.hpp>
#include <type_traits>

int main(){
    using namespace boost::hana;
    auto a = make_set(1,'a',3);
    auto b = remove_if(a, [](const auto& x){return bool_c<std::is_same_v<decltype(x), char>>;});
    // expects b to be make_set(1, 3);
}

This results in a static_assert failure. It tells me:

static assertion failed: hana::remove_if(xs, predicate) requires 'xs' to be a MonadPlus

         static_assert(hana::MonadPlus<M>::value, 

Why does this fail? Why can't a set be a MonadPlus, even though the empty set and the concatenation operation is defined?


Solution

  • If you want to filter a list you should use hana::tuple. The idea of a Set is more general than MonadPlus or even Monad. Your assertion that hana::set has implementations for hana::concat or hana::empty is incorrect, and besides it also requires the data structure to be a Monad.

    Base on your example, perhaps what you are looking for is hana::difference, but hana::set itself is pretty useless for this. It's operations require hana::Comparable and don't play well with run-time state.

    If you want to "filter" sets by their type and maintain run-time values, I suggest using hana::map which also supports set operations. You can make its keys the hana::type of its value. This still requires uniqueness by its type.

    Here is an example:

    #include <boost/hana.hpp>
    
    namespace hana = boost::hana;
    
    constexpr auto typed_set = [](auto&& ...x) {
      return hana::make_map(
        hana::make_pair(hana::typeid_(x), std::forward<decltype(x)>(x))...
      );
    };
    
    int main() {
      auto a = typed_set(1, 'a', 3.0f);
      auto b = typed_set('c');
      auto c = hana::difference(a, b);
    
      BOOST_HANA_RUNTIME_ASSERT(c == typed_set(1, 3.0f));
    }