Type inference for lambda function

Hi cafe, today while explaining lambda functions I came across this case which I don't really understand. This is what I get using ghci λ> :t (\a b -> a + b) (\a b -> a + b) :: Num a => a -> a -> a but when loading a .hs file with just this function (without signature) add = \a b -> a + b I get λ> :t add add :: Integer -> Integer -> Integer but I can explicitly define add to have this type: add :: Num a => a -> a -> a add = \a b -> a + b Also ghc infers this polymorphic type for example for add0 a = \b -> a + b Why is the type of add without a signature inferred to Integer and not Num a? Best regards Matthias

On Thu, Oct 21, 2021 at 08:40:45PM +0200, Matthias Güdemann wrote:
This is what I get using ghci
λ> :t (\a b -> a + b) (\a b -> a + b) :: Num a => a -> a -> a
but when loading a .hs file with just this function (without signature)
add = \a b -> a + b
I get
λ> :t add add :: Integer -> Integer -> Integer
Compare: $ ghci -XNoMonomorphismRestriction λ> x = \a b -> a + b λ> :t x x :: Num a => a -> a -> a with $ ghci -XMonomorphismRestriction λ> x = \a b -> a + b λ> :t x x :: Integer -> Integer -> Integer -- Viktor.

Hi David and Victor, thanks for pointing this out. That explains it. best regards Matthias

This is the dreaded monomorphism restriction, which is turned on by default
in modules but turned off by default in GHCi. Because your function is not
"syntactically" a function (i.e., there are no arguments to the left of the
= sign), and has no type signature, the type checker insists on it having a
monomorphic type. Unless there's a use of it in the module forcing it to a
particular type, the defaulting rules come into play, and a variable with a
`Num` constraint defaults, by default, to `Integer`.
On Thu, Oct 21, 2021, 2:41 PM Matthias Güdemann
Hi cafe,
today while explaining lambda functions I came across this case which I don't really understand.
This is what I get using ghci
λ> :t (\a b -> a + b) (\a b -> a + b) :: Num a => a -> a -> a
but when loading a .hs file with just this function (without signature)
add = \a b -> a + b
I get
λ> :t add add :: Integer -> Integer -> Integer
but I can explicitly define add to have this type:
add :: Num a => a -> a -> a add = \a b -> a + b
Also ghc infers this polymorphic type for example for
add0 a = \b -> a + b
Why is the type of add without a signature inferred to Integer and not Num a?
Best regards Matthias _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Oct 21, 2021, at 2:48 PM, David Feuer
wrote: This is the dreaded monomorphism restriction, which is turned on by default in modules but turned off by default in GHCi. Because your function is not "syntactically" a function (i.e., there are no arguments to the left of the = sign), and has no type signature, the type checker insists on it having a monomorphic type. Unless there's a use of it in the module forcing it to a particular type, the defaulting rules come into play, and a variable with a `Num` constraint defaults, by default, to `Integer`.
Indeed -- but I will offer a tiny nuance: the type checker only insists that the type has no constraints, not that it is fully monomorphic. That is, writing `myId = id` will produce a polymorphic myId because there are no constraints on id, while `myPlus = (+)` will be monomorphic because of the constraint on (+). Richard

The MonomorphismRestriction should be eliminated from the next Haskell standard. Am 26.10.21 um 15:53 schrieb Richard Eisenberg:
On Oct 21, 2021, at 2:48 PM, David Feuer
wrote: This is the dreaded monomorphism restriction, which is turned on by default in modules but turned off by default in GHCi. Because your function is not "syntactically" a function (i.e., there are no arguments to the left of the = sign), and has no type signature, the type checker insists on it having a monomorphic type. Unless there's a use of it in the module forcing it to a particular type, the defaulting rules come into play, and a variable with a `Num` constraint defaults, by default, to `Integer`.
Indeed -- but I will offer a tiny nuance: the type checker only insists that the type has no constraints, not that it is fully monomorphic. That is, writing `myId = id` will produce a polymorphic myId because there are no constraints on id, while `myPlus = (+)` will be monomorphic because of the constraint on (+). -- I would rather have questions that cannot be answered, than answers that cannot be questioned. -- Richard Feynman

It would silently change behavior of some programs, yes. It could also make
them slower under some circumstances. But anyone relying on the
monomorphism restriction to choose types for their program should change
their ways regardless. Top level type signatures aren't that hard to write.
On Sun, Oct 31, 2021, 11:58 PM Andreas Källberg
On 1 Nov 2021, at 02:16, Ben Franksen
wrote: The MonomorphismRestriction should be eliminated from the next Haskell standard.
Wait, what? Wouldn't that silently change behaviour of programs and make them slower?
Andreas _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Mon, Nov 01, 2021 at 12:54:47AM -0400, David Feuer wrote:
It would silently change behavior of some programs, yes. It could also make them slower under some circumstances. But anyone relying on the monomorphism restriction to choose types for their program should change their ways regardless. Top level type signatures aren't that hard to write.
It isn't just, or even primarily top level signatures. It is more often let bindings that will lack a signature, and frankly I'd rather have the restriction in place. When your type fails to generalise and code does not compile, the solution is not hard to find. When let-bound expressions cease to be CAFs, and start being evaluated repeatedly, rather than just once, that's substantially harder to find and diagnose. -- Viktor.

Am 01.11.21 um 06:10 schrieb Viktor Dukhovni:
On Mon, Nov 01, 2021 at 12:54:47AM -0400, David Feuer wrote:
It would silently change behavior of some programs, yes. It could also make them slower under some circumstances. But anyone relying on the monomorphism restriction to choose types for their program should change their ways regardless. Top level type signatures aren't that hard to write.
It isn't just, or even primarily top level signatures. It is more often let bindings that will lack a signature, and frankly I'd rather have the restriction in place.
When your type fails to generalise and code does not compile, the solution is not hard to find. When let-bound expressions cease to be CAFs, and start being evaluated repeatedly, rather than just once, that's substantially harder to find and diagnose.
Good point. I have a question, since the wording in the Haskell 2010 Report is pretty complicated: Is it really guaranteed that a declaration of the form var = closed_expr (where closed_expr contains no free variables) without a type signature compiles to a CAF? I.e. is evaluated only once? Assuming this is is the case I admit there is justification for the MR. Other solutions are possible, though, for instance the compiler could issue a warning, ideally together with a hint that you may want to add a type signature to make your intention clear: either you want a CAF or the most general type; you can't have both. Cheers Ben -- I would rather have questions that cannot be answered, than answers that cannot be questioned. -- Richard Feynman

Changing it into a warning might not be a bad idea at all. It is a common problem for beginners after all. The only risk is if it would force you to write a lot more type signatures in order to avoid the warning. Another option would be to improve error messages around it so ghc can suggest either adding an explicit type signature or disabling the restriction. This might be difficult to implement though, since the error usually happens when type checking something else.
On 1 Nov 2021, at 15:10, Ben Franksen
wrote: Am 01.11.21 um 06:10 schrieb Viktor Dukhovni:
On Mon, Nov 01, 2021 at 12:54:47AM -0400, David Feuer wrote: It would silently change behavior of some programs, yes. It could also make them slower under some circumstances. But anyone relying on the monomorphism restriction to choose types for their program should change their ways regardless. Top level type signatures aren't that hard to write. It isn't just, or even primarily top level signatures. It is more often let bindings that will lack a signature, and frankly I'd rather have the restriction in place. When your type fails to generalise and code does not compile, the solution is not hard to find. When let-bound expressions cease to be CAFs, and start being evaluated repeatedly, rather than just once, that's substantially harder to find and diagnose.
Good point. I have a question, since the wording in the Haskell 2010 Report is pretty complicated:
Is it really guaranteed that a declaration of the form
var = closed_expr
(where closed_expr contains no free variables) without a type signature compiles to a CAF? I.e. is evaluated only once?
Assuming this is is the case I admit there is justification for the MR.
Other solutions are possible, though, for instance the compiler could issue a warning, ideally together with a hint that you may want to add a type signature to make your intention clear: either you want a CAF or the most general type; you can't have both.
Cheers Ben -- I would rather have questions that cannot be answered, than answers that cannot be questioned. -- Richard Feynman
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

I'd be against just making it a warning. It means that you'd get a warning every time you have `let n = 3` in your code. Killing the MR without a warning is even worse: it means you'd silently get re-evaluation every time you have `let n = m1 + m2` in your code. I will note that GHC supports -Wmonomorphism-restriction, which is exactly the warning that is requested in this thread, for those that want it. Richard
On Nov 3, 2021, at 8:46 PM, Andreas Källberg
wrote: Changing it into a warning might not be a bad idea at all. It is a common problem for beginners after all. The only risk is if it would force you to write a lot more type signatures in order to avoid the warning.
Another option would be to improve error messages around it so ghc can suggest either adding an explicit type signature or disabling the restriction. This might be difficult to implement though, since the error usually happens when type checking something else.
On 1 Nov 2021, at 15:10, Ben Franksen
wrote: Am 01.11.21 um 06:10 schrieb Viktor Dukhovni:
On Mon, Nov 01, 2021 at 12:54:47AM -0400, David Feuer wrote: It would silently change behavior of some programs, yes. It could also make them slower under some circumstances. But anyone relying on the monomorphism restriction to choose types for their program should change their ways regardless. Top level type signatures aren't that hard to write. It isn't just, or even primarily top level signatures. It is more often let bindings that will lack a signature, and frankly I'd rather have the restriction in place. When your type fails to generalise and code does not compile, the solution is not hard to find. When let-bound expressions cease to be CAFs, and start being evaluated repeatedly, rather than just once, that's substantially harder to find and diagnose.
Good point. I have a question, since the wording in the Haskell 2010 Report is pretty complicated:
Is it really guaranteed that a declaration of the form
var = closed_expr
(where closed_expr contains no free variables) without a type signature compiles to a CAF? I.e. is evaluated only once?
Assuming this is is the case I admit there is justification for the MR.
Other solutions are possible, though, for instance the compiler could issue a warning, ideally together with a hint that you may want to add a type signature to make your intention clear: either you want a CAF or the most general type; you can't have both.
Cheers Ben -- I would rather have questions that cannot be answered, than answers that cannot be questioned. -- Richard Feynman
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.
participants (6)
-
Andreas Källberg
-
Ben Franksen
-
David Feuer
-
Matthias Güdemann
-
Richard Eisenberg
-
Viktor Dukhovni