Understanding instances

It’s evidently useful to know that some arbitrary structure is a monoid, functor or anything else because it let’s you know what it is basically capable of (or rather, what it can’t do).

However, whenever I start using a library I look up what the central structure is an instance of I still need to look at the implementation of each to see exactly what mconcat, fmap or bind does. Is this always needed?

It’s unlikely you really need to look at the implementation of fmap because there’s at most one valid fmap for any given type.

2 Likes

Instead of looking of the implemention, you might look at the type and try to figure out whith concrete example what can be the output ?

For example mconcat :: Monoid a => [a] -> a. What could the value ofmconcat ["foo", "bar"] be ?

In this case it’s easy, because I know the implementation. I’m thinking of cases where it is foreign, such as in a library one might have found.

It’s unlikely you really need to look at the implementation of fmap because there’s at most one valid fmap for any given type.

Wouldn’t (Int, Int) have at least two valid implementations?

ghci> fmap (+2) (1,2)
(1,4)
ghci> xmap f (a,b) = (f a, b)
ghci> xmap (+2) (1,2)
(3,2)

(Int, Int) has kind *, and thus isn’t the sort of thing that has a Functor instance.

(,) Int has kind * -> *, and thus can have a Functor instance, and there’s only one way to implement fmap for that.

4 Likes

Good documentation preserves abstraction boundaries, conveys user-oriented expectations, and relieves you from the rabbit hole of following implementations.

Good documentation is unincentivized in programming, however. That’s why.

1 Like