Note to self about deriving the
Profunctor typeclass. Source is here: here.
This is a literate Haskell file, and it can be built using Stack:
git clone https://github.com/carlohamalainen/playground.git cd playground/haskell/profunctors stack build
stack ghci instead of
cabal repl. The main executable is
in a path like
This blog post follows some of the examples from I love profunctors.
First, some extensions and imports:
The basic problem here is to write a function that capitalizes each word in a string. First, write a function that capitalizes a single word:
The straightforward solution (ignoring the loss of extra spaces
between words since
unwords . words is not an isomorphism)
is to use this composition:
*Profunctors> capitalize "hey yo WHAT DID THIS DO?" "Hey Yo What Did This Do?"
Why stop here? Let’s generalise the
function by factoring out the
unwords functions. Call them
u and make them arguments:
capitalize ≡ capitalize1 words unwords.
We may as well factor out
map capWord as well:
capitalize ≡ capitalize2 words unwords (map capWord).
Now look at the types - there is no reason to be restricted
[String] so use the most
general types that make the composition
u . f . w work:
w f u c -------> d -------> b -------> d
w :: c -> d and similar for
u. This lets us write
Next, we can generalize the type of
f. To help with this step, recall
-> is a functor (there is an instance
Functor (->)) so write
the last two types in the signature with prefix notation:
Now we can use a general functor
h instead of
Naturally this won’t work because the type signature has the
h but the body of
is using function composition (the
.) as the type error
| Couldn't match type ‘h’ with ‘(->)’ || ‘h’ is a rigid type variable bound by || the type signature for || capitalize3' :: (c -> a) -> (b -> d) -> h a b -> h c d || Expected type: h c d || Actual type: c -> d
Fortunately for us, we can make a typeclass that captures the behaviour that we want. We have actually arrived at the definition of a profunctor.
Now we can write the capitalize function using
a typeclass constraint on
Profunctor which lets us
dimap function instead
of explicit function composition:
This is overkill for the capitalization problem, but it shows how structure can come out of simple problems if you keep hacking away.