7

In ghci I can do this:

ghci> (fmap . const) 5 [1,2,3,4,5]
[5,5,5,5,5]

but if I try to extract the sub-expression (fmap . const) into a variable I get an error:

ghci> let foo = (fmap . const)

<interactive>:3:12:
    No instance for (Functor f0) arising from a use of `fmap'
    The type variable `f0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Functor ((,) a) -- Defined in `GHC.Base'
      instance Functor ((->) r) -- Defined in `GHC.Base'
      instance Functor IO -- Defined in `GHC.Base'
      ...plus two others
    In the first argument of `(.)', namely `fmap'
    In the expression: (fmap . const)
    In an equation for `foo': foo = (fmap . const)

I thought this might mean GHC's type inference was failing somehow, but when I ask ghci for the type of the subexpresiion in isolation it has no problem:

ghci> :t (fmap . const)
(fmap . const) :: Functor f => b -> f a -> f b

So what's going on here? Is there any way to extract this subexpression into a variable? Why doesn't the straightforward let work?

Update:

"What is the monomorphism restriction" might be a good thing to link to in an answer (ie: "See also..."), but this isn't a duplicate of that question unless StackOverflow has become some sort of weird version of the game show Jeopardy. I already knew of the monomorphism restriction when I asked this question, but it was not at all obvious to me that MR was the cause of the error I was receiving.

The answer to that question doesn't help in that regard. It says "What this means is that, in some circumstances, if your type is ambiguous ... the compiler will choose to instantiate that type to something not ambiguous", which almost the opposite of what is happening here (MR is creating ambiguity, not removing it.)

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • 3
    Try `:set -XNoMonomorphismRestriction` first, then search SO for "monomorphism restriction". This problem crops up pretty frequently. – bheklilr Jun 27 '14 at 17:00
  • @bheklilr You should post that as an answer. If you think the issue crops up frequently you can write a detailed answer to this question and use it as a reference answer to close the other for duplicates. – Bakuriu Jun 27 '14 at 17:03
  • 3
    I would be interested to know what percentage of Haskell SO questions are answered with "It's the monomorphism restriction". It seems to come up a lot. – Tom Ellis Jun 27 '14 at 17:08
  • @bheklilr I've run into the monomorphism restriction before, but don't understand how it applies here. – Laurence Gonsalves Jun 27 '14 at 18:16
  • Because you specified a function definition in point free form, an explicit type signature is needed for the compiler to figure out exactly what you mean. With the restriction off, the compiler simply chooses the most general type it can for the expression. In 99% of the cases, the monomorphism restriction is a hindrance and does not help the programmer write code. There are a few subtle instances where it makes sense, but most people will not come across those cases or notice if they do. You can read more about it in the [wiki](http://www.haskell.org/haskellwiki/Monomorphism_restriction) – bheklilr Jun 27 '14 at 18:49
  • @bheklilr Now I am even more confused. The wiki says "to a first approximation it means that you often cannot overload a function unless you provide an explicit type signature", but as you've indicated it's possible to this requirement goes away as long as the function definition is not point free. In particular, `let foo x = fmap (const x)` works without having to provide an explicit type signature. Is there a "less approximate" explanation of when MR kicks in? – Laurence Gonsalves Jun 27 '14 at 21:11
  • @LaurenceGonsalves The [Haskell 98 Report](http://www.haskell.org/onlinereport/decls.html#monomorphism) has the definition and probably the most technical and precise discussion of it that you'll find. – bheklilr Jun 27 '14 at 21:19
  • @bheklilr Also the [Haskell 2010 Report](http://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-930004.5.5). – Ørjan Johansen Jun 28 '14 at 00:13
  • 2
    @LaurenceGonsalves: it helps to know what the MR is supposed to do. Type classes are implemented (in ghc at least) as an implicit argument to a function. When you create a binding without any arguments or a type signature, the MR pessimistically assumes you meant to write a shared monomorphic value rather than an unshared polymorphic value (unshared because it's actually a function that takes an implicit argument applied at the use site). This means the MR applies whenever you write code that doesn't look like a function (no arguments, no type) but actually is. – John L Jun 28 '14 at 06:41
  • 1
    possible duplicate of [What is the monomorphism restriction?](http://stackoverflow.com/questions/32496864/what-is-the-monomorphism-restriction) – Bakuriu Sep 11 '15 at 09:10

1 Answers1

5

It's the dreaded monomorphism restriction. If you run :set -XNoMonomorphismRestriction it will work. You can also define it with an explicit type signature like this

foo :: Functor f => b -> f a -> f b
foo = fmap . const
Dirk Holsopple
  • 8,731
  • 1
  • 24
  • 37
  • Are errors of the form "no instance for ... the type variable is ambiguous" usually due to the monomorphism restriction? – Dan Jun 27 '14 at 17:08
  • 2
    It's a common case they arise, but there are many other reasons as well. Most are more legitimate so you can't just assume. – J. Abrahamson Jun 27 '14 at 19:44
  • Can you explain why `let foo = fmap . const :: Functor f => b -> f a -> f b` is rejected too? – dfeuer Jun 28 '14 at 05:19
  • 3
    @dfeuer: in that case, the type signature applies to the expression `fmap . const`, and there is still no type signature for `foo`, which is the identifier to which the restriction is applied. In essence, this type signature doesn't tell the type checker anything new, whereas adding a type signature directly to `foo` does tell the type checker something new (i.e. "I really do mean for this to be polymorphic even though it doesn't look like a function") – John L Jun 28 '14 at 06:34