Reputation: 1479
Lately I've been playing around with Haskell, and specifically the whole functors concept. The more I dive into it, the more a-ha moments I'm getting, and it certainly tickles my dopamine receptors quite a bit.
The problem I'm stuck with is the following. Here's the code that works, it lifts the function and then applies it first to the IO value, and then to a List.
replicator1 =
fmap (replicate 3)
replicator2 =
fmap (replicate 3)
main = do
replicated <- replicator1 getLine
print (replicator2 replicated)
It is very tempting to write it in a more concise manner, i.e.:
replicator =
fmap (replicate 3)
main = do
replicated <- replicator getLine
print (replicator replicated)
Part of me says it's conceptually right, since replicator
should be applyable both to IO and to List instances, but being a strongly typed language Haskell doesn't allow me to do so. I think I pretty much understand why is this happening.
The question is: is there any way I can get any closer to the latter variant? Or is it fine to live with the former?
Thank you!
Upvotes: 8
Views: 122
Reputation: 68172
Your code is actually fine, except you ran into the dreaded monomorphism restriction which kept Haskell from inferring the most general possible type for replicator
.
Essentially, with the restriction on, Haskell will not infer polymorphic types for bindings that do not look like functions. This means it has to pick a concrete functor like []
or IO
for replicate
and causes an error if you try to use it in two different contexts.
You can make your code work in three ways:
turn off the monomorphism restriction: add {-# LANGUAGE NoMonomorphismRestriction #-}
at the top of your module.
make replicator
look like a function:
replicator x = fmap (replicate 3) x
add explicit type signatures to your code
replicator :: Functor f => f a -> f [a]
replicator = fmap (replicate 3)
The third option is the most idiomatic. Good Haskell style involves adding explicit type signatures to all of your top-level identifiers. However, it's useful to know the other two options to understand what's going on and to be able to write quick-and-dirty throwaway scripts in Haskell without worrying about type signatures.
Upvotes: 19