Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does GHC infer a monomorphic type here, even with MonomorphismRestriction disabled?

This was prompted by Resolving the type of `f = f (<*>) pure`, which discusses a more complicated example, but this one works too.

The following definition compiles without problem:

w :: Integral a => a
w = fromInteger w

...Of course it doesn't work runtime-wise, but that's beside the question. The point is that the definition of w itself uses a specialised version of w :: Integer. Clearly that is a suitable instantiation, and therefore typechecks.

However, if we remove the signature, then GHC infers not the above type, but only the concrete one:

w' = fromInteger w'
GHCi> :t w
w :: Integral a => a
GHCi> :t w'
w' :: Integer

Well, when I saw this, I was fairly sure this was the monomorphism restriction at work. It's well known that also e.g.

i = 3
GHCi> :t i
i :: Integer

although i :: Num p => p would be perfectly possible. And indeed, i :: Num p => p is inferred if -XNoMonomorphismRestriction is active, i.e. if the monomorphism restriction is disabled.

However, in case of w' only the type Integer is inferred even when the monomorphism restriction is disabled.

To count out that this has something to do with defaulting:

fromFloat :: RealFrac a => Float -> a
q :: RealFrac a => a
q = fromFloat q
q' = fromFloat q'
GHCi> :t q
q :: RealFrac a => a
GHCi> :t q'
q' :: Float

Why is the polymorphic type not inferred?

like image 260
leftaroundabout Avatar asked Mar 28 '19 16:03

leftaroundabout


1 Answers

Polymorphic recursion (where a function calls itself at a different type than the one at which it was called) always requires a type signature. The full explanation is in Section 4.4.1 of the Haskell 2010 Report:

If a variable f is defined without providing a corresponding type signature declaration, then each use of f outside its own declaration group (see Section 4.5) is treated as having the corresponding inferred, or principal type. However, to ensure that type inference is still possible, the defining occurrence, and all uses of f within its declaration group must have the same monomorphic type (from which the principal type is obtained by generalization, as described in Section 4.5.2).

The same section later presents an example of polymorphic recursion supported by a type signature.

My understanding is that unaided type inference is generally undecidable in the presence of polymorphic recursion, so Haskell doesn't even try.

In this case, the type checker starts with

w :: a

where a is a meta-variable. Since fromInteger is called with w as an argument within its own declaration (and therefore within its declaration group), the type checker unifies a with Integer. There are no variables left to generalize.

A slight modification of your program gives a different result for the same reason:

v = fromIntegral v

By your original reasoning, Haskell would infer v :: forall a. Num a => a, defaulting the v on the RHS to type Integer:

v :: forall a. Num a => a
v = fromIntegral (v :: Integer)

But instead, it starts with v :: a. Since v is passed to fromIntegral, it imposes Integral a. Finally, it generalizes a. In the end, the program turns out to be

v :: forall a. Integral a => a
v = fromIntegral (v :: a)
like image 177
dfeuer Avatar answered Sep 29 '22 12:09

dfeuer