What is the canonical way of updating a value (given a key and a new value) inside a boost::hana::map
?
I tried using boost::hana::replace_if
but it does not work on map
as it is not a Functor
- I can get it to work by converting the map
to a tuple
and then back to a map
, but it sounds inefficient.
The alternative I'm currently using is calling map::erase_key
followed by map::insert
.
Is there any replace
or update
function designed for this purpose that I might be missing? Or is this the "canonical" way of updating a value?
I don't think there is currently a canonical way to do this. If there are valid use cases for this, perhaps we could get a function in there to support it. The problem with hana::erase_key
is that you will be creating a new map and then again with hana::insert
. For the time being, using hana::unpack
and then creating a new map is probably your best bet.
#include <boost/hana.hpp>
namespace hana = boost::hana;
template <typename NewPair>
struct replace_helper_t
{
NewPair const& new_pair;
template <typename Pair>
constexpr decltype(auto) operator()(Pair&& p) const
{
return hana::if_(
hana::equal(hana::first(new_pair), hana::first(p)),
new_pair,
std::forward<Pair>(p)
);
}
};
struct replace_t
{
template <typename Map, typename NewPair>
constexpr auto operator()(Map&& m, NewPair&& new_pair) const
{
return hana::unpack(std::forward<Map>(m),
hana::on(
hana::make_map,
replace_helper_t<NewPair>{std::forward<NewPair>(new_pair)}
)
);
}
};
constexpr replace_t replace{};
int main()
{
auto my_map = hana::make_map(
hana::make_pair(hana::int_c<7>, 7),
hana::make_pair(hana::int_c<13>, 13),
hana::make_pair(hana::int_c<23>, 23)
);
auto new_map = replace(my_map, hana::make_pair(hana::int_c<13>, 14.0f));
BOOST_HANA_RUNTIME_ASSERT(new_map ==
hana::make_map(
hana::make_pair(hana::int_c<7>, 7),
hana::make_pair(hana::int_c<13>, 14.0f),
hana::make_pair(hana::int_c<23>, 23)
)
);
}
On another note, perhaps hana::map
should be a Functor
.
Do you need to change the type of the value? If not, and if your value can be assigned to, you can use map[key] = new_value
or equivalently hana::at_key(map, key) = new_value
, as hana::at_key
returns a reference.
If you need the type of the value to change, that's more tricky. We won't be able to do anything in-place, because the type of the map after replacing the value will be different from its type before replacing the value. Hence, we have to create a new map or some kind of modified view of that map (but views are not currently supported). Using erase_key
and insert
will indeed cause two maps to be created, which is inefficient. Instead, we could provide some kind of update
function that would achieve the same, but would create only one copy of the map (the result). I believe we could also do better than erase_key
+ insert
in terms of compilation times by providing our own function. I opened this issue to keep track of your request, since I think it is important to provide something like this; thanks.
Finally, I'd like to comment what Jason said:
On another note, perhaps hana::map should be a Functor.
hana::map
could be made a Functor, but I'm not sure that transform
could touch the keys of the map while still respecting the Functor laws. If that's not the case, you still wouldn't be able to say "replace the value with XXX if the key satisfies some predicate" using e.g. hana::replace_if
. I tried (but failed so far) to find an example of functions that would break the laws if hana::transform
basically transformed the sequence of underlying key/value pairs, but my intuition is that it's possible to find such an example. To be continued in issue #278.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With