1

Example 1

Following definition without the type declaration will throw an error:

f :: Eq t => (t,t) -> Bool  -- omiting this line will result in an error
f = \(x,y) -> x==y

(I know this function can be written shorter, but this is not the point here.)

Example 2

On the other hand using the same lambda function in a function using map does work without producing an error:

 g l = map (\(x,y) -> x==y) l

(Just as an illustration: g [(3,4),(5,5),(7,6)] will produce [False,True,False]

Example 3

Also following code is perfectly fine and it seems to do exactly the same as the original f from above. Here the type inference seems to work.

 f' (x,y) = x==y

Question

So my question is: Why do we need a type declaration in the first case, but not in the second and in the third?

flawr
  • 10,814
  • 3
  • 41
  • 71

2 Answers2

4

If you use:

{-# LANGUAGE NoMonomorphismRestriction #-}

f = \(x,y) -> x==y

you won't get the error.

Update

The Haskell Wiki page on Monomorphism Restriction (link) offers some details on why these definitions are treated differently:

f1 x = show x

f2 = \x -> show x

The difference between the first and second version is that the first version binds x via a "function binding" (see section 4.4.3 of the Haskell 2010 Report), and is therefore unrestricted, but the second version does not. The reason why one is allowed and the other is not is that it's considered clear that sharing f1 will not share any computation, and less clear that sharing f2 will have the same effect. If this seems arbitrary, that's because it is. It is difficult to design an objective rule which disallows subjective unexpected behaviour. Some people are going to fall foul of the rule even though they're doing quite reasonable things.

ErikR
  • 51,541
  • 9
  • 73
  • 124
2

As @ErikR notes in the comments, this is due to the Monomorphism restriction. We also see this in the error message:

No instance for (Eq a0) arising from a use of '=='
The type variable 'a0' is ambiguous
Possible cause: the monomorphism restriction applied to the following:
f :: (a0, a0) -> Bool
(bound at ...
Note: there are several potential instances:
instance Eq a => Eq (GHC.Real.Ratio a) -- Defined in 'GHC.Real'
instance Eq () -- Defined in 'GHC.Classes'
instane (Eq a, Eq b) => Eq (a,b) -- Defined in 'GHC.Classes'
..plus 22 others

The monomorphism restriction implies that the compiler tries to instantiate an ambiguous type into a non-ambiguous type. (Source: What is the monomorphism restriction?).

So, Haskell wants to put a single instance, but it can't - it finds several, and doesn't know which to choose.

This explains why adding the type solves the problem: now the compiler knows what to choose.

The "monomorphism restriction" is a counter-intuitive rule in Haskell type inference. If you forget to provide a type signature, sometimes this rule will fill the free type variables with specific types using "type defaulting" rules.

Community
  • 1
  • 1
  • 1
    The thing is that if you write `f (x,y) = x == y` the monomorphism restriction doesn't get in the way, and I'm trying to figure out why. – ErikR May 11 '16 at 18:07
  • The Haskell Wikia on the subject starts with an similar example. It looks as if the monomorphism restriction comes into play when there are lambda expressions that aren't bound to other items. Now studying the spec. – S.L. Barth is on codidact.com May 11 '16 at 18:20
  • 1
    @ErikR That's a design choice. The goal of the MR is to avoid turning non-functions into functions. E.g. `let x = product [1..500] in ...` without the MR defines a runtime function taking a hidden `Num a` argument. This implies that each use of `x` triggers 500 multiplications, with surprising performance penalties. This repeated evaluation could turn a linear recursive algorithm into an exponential one, IIRC. So, the MR "forces" non-function bindings `let x = e` to resolve all constraints at once, limiting polymorphism. – chi May 11 '16 at 18:29
  • 1
    @ErikR The weird thing is that only the binding type is inspected by the MR. If we write `let x = e`, then `x` is monomorphic even if `e` has a function type and adding a hidden dictionary argument would not have been so bad. The designers of Haskell apparently chose against the special case, making `let f y = ...` different from `let f = \y -> e` at type-check time. Later, they discovered that in practice this problem almost never happens (running a Hackage statistic), so there's hope that the MR will be turned off sometime in the future. – chi May 11 '16 at 18:31