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!
It is a high level concept of implementing polymorphism. According to Haskell developers, all the Types such as List, Map, Tree, etc. are the instance of the Haskell Functor. A Functor is an inbuilt class with a function definition like −
In the above example, fmap () is a generalized representation of the function map (). In the following example, we will see how Haskell Functor works. Here, we have used both map () and fmap () over a list for a subtraction operation.
An Applicative Functor is a normal Functor with some extra features provided by the Applicative Type Class. Using Functor, we usually map an existing function with another function defined inside it.
The Functor typeclass represents the mathematical functor: a mapping between categories in the context of category theory. In practice a functor represents a type that can be mapped over. See also Applicative functor which is a special case of Functor
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.
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