Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell type inference for Functors

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!

like image 849
SkyWriter Avatar asked Aug 19 '15 05:08

SkyWriter


People also ask

What is functor in Haskell?

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 −

What is FMAP () in Haskell functor?

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.

What is an applicative functor in Python?

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.

What is a functor typeclass?

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


1 Answers

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.

like image 107
Tikhon Jelvis Avatar answered Sep 22 '22 22:09

Tikhon Jelvis