Hacker News new | ask | show | jobs
by sfvisser 510 days ago
The generalized version of ‘traverse/mapM’ that doesn’t just work for lists, but any ‘Traversable’ type is absolutely amazing and is useful in so many cases.

‘traverse :: Applicative f => (a -> f b) -> t a -> f (t b)’

And you can derive it for free for your own datatypes!

The amount of code I’ve manually written in other languages to get a similar effect is painfully large.

2 comments

The fully qualified type is

    traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
and deriving it for your own types is as simple as

    data MyType ... = ... deriving (Traversable)
Correct! I simply copied the definition from the type class, but the context is important.
Can you kindly make a real word example that is not usual Maybe or Either example, that uses user defined data type?

I understand how Applicative works, but I don’t know how to apply (pun intended) to my data types.

I had a generic report class that essentially fetched a bunch of database rows, did some stuff for each row, and then combined the results together into a report. (This was in Scala, I know Haskell doesn't have classes, but presumably similar situations can happen)

For one client, we needed to accumulate some extra statistics for each. For another, we needed to call their web API (so async I/O) to get some of the data used in the report. By making the generic superclass use a generic Applicative type, we could keep the report business logic clear and allow the client-specific subclasses to do these client-specific things and have them compose the right way.

Wanting custom applicative types is rarer than using a standard one, but it can be a good way to represent any kind of "secondary effect" or "secondary requirement" that your functions might have. E.g. "requires this kind of authorisation" or "must happen in a database transaction". But a lot of the time you can implement custom things using reader/writer/state, or free, rather than having to write a completely from-scratch applicative.

Note that deriving Traversable for you own datatypes mean changing the structure to map effect over, the `t` variable. Not the effect `f`, which is generic and the Monad/Applicative in this case.

Besides Maybe/Either `t` could represent anything, like container types Lists/Trees/Hashmaps etc, but also more complicated structures like syntax trees for programming languages or custom DSLs. Or (my favorite use case!) recursive command types for robot control. I'm doing this mostly in Rust, but borrow all the ideas from Haskell.