ScopedTypeVariables in let-bindings (not where-bindings!) and bug 4347

Hi all, I have some problem with ScopedTypeVariables, let and so on, at least in GHC 7. My aim is to be able to supply a polymorphic type signature in let bindings. Confusingly, I find no such example in the blog post about local let generalization [1]. I first met this problem when porting some test-code from Kiselyov, when refusing to convert its let- bindings into where-bindings (which, btw, can't be typed into GHCi). I've also added a comment to the bug report, but it's still quite possible that I'm missing something. == Summary == My starting point today: I wanted to test polymorphic recursion by entering this definition into GHCi: a :: a -> a a = a a This is accepted without extra language pragmas but only with the type signature, due to the use of polymorphic recursion for which type inference is undecidable. To type it into GHCi I need to supply a polymorphic type in a let- binding. However, it turns out that Prelude> let id2 :: Int -> Int = \x -> x works but there's no way whatsoever to make the following work: Prelude> let (id2 :: forall t. t -> t) = \x -> x because of bug 4347. But let's start from the beginning. == Walk-through, with various other complaints. == So let's try this: $ ghci -XScopedTypeVariables Prelude> let a :: forall b. b -> b = a a [...request of enabling -XRank2Types or -XRankNTypes, confusing or BUGGY because the type is rank-1 ...] Prelude> :set -XRank2Types Prelude> let a :: forall b. b -> b = a a <interactive>:1:31: Couldn't match expected type `forall b. b -> b' with actual type `b0 -> b0' In the first argument of `a', namely `a' In the expression: a a In a pattern binding: a :: forall b. b -> b = a a Uh? That's strange. Seems an instance of bug #4347, but the error message is not yet the same. To get that, let's make an easier example: id. Of course, it needs no type signature, but if one can't give to this an explicit signature, realistic examples are hopeless. So, at the GHCi 7.0.3 prompt, I can't type: Prelude> let id2 :: t -> t = \x -> x GHCi first suggests using -XScopedTypeVariables. Let's try that: Prelude> :set -XScopedTypeVariables Prelude> let (id2 :: Int -> Int) = \x -> x This monomorphic function is fine, but the original polymorphic one is not accepted, it complains that t is not in scope - fine, let's add forall: *Main> let (id :: forall t. t -> t) = \x -> x <interactive>:1:6: Illegal polymorphic or qualified type: forall t. t -> t Perhaps you intended to use -XRankNTypes or -XRank2Types In a pattern type signature: forall t. t -> t In the pattern: id :: forall t. t -> t In a pattern binding: (id :: forall t. t -> t) = \ x -> x This is already confusing for me - the intended type is rank-1. But let's listen to GHCi again: Prelude> :set -XRank2Types Now, the error message is really puzzling: Prelude> let (id2 :: forall t. t -> t) = \x -> x <interactive>:1:33: Couldn't match expected type `t0 -> t1' with actual type `forall t. t -> t' The lambda expression `\ x -> x' has one argument, but its type `forall t. t -> t' has none In the expression: \ x -> x In a pattern binding: (id2 :: forall t. t -> t) = \ x -> x What's up? The documentation never mention let, but it does mention type signatures for patterns [2], and in "let a = b in c", "a" is IIRC a considered a pattern. Still, the lack of examples with let is already confusing. id2 :: forall t. t -> t = \x -> x How does "forall t. t -> t" have no argument? At worst, it expects a type-argument, in its System F representation . The only implementation problem I see in this code is that unification must "see through" the forall. I've seen this behavior described [3, 4], but all the problematic examples have been fixed in some way. Here, I see no direct fix. Best regards Paolo G. Giarrusso [1] http://hackage.haskell.org/trac/ghc/blog/LetGeneralisationInGhc7 [2] http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/other-type-extensions... [3] http://stackoverflow.com/questions/5885479/weird-error-when-using-scoped-typ... [4] http://hackage.haskell.org/trac/ghc/ticket/4347

On May 21, 2011, at 8:55 AM, Paolo G. Giarrusso wrote:
Hi all, I have some problem with ScopedTypeVariables, let and so on, at least in GHC 7. My aim is to be able to supply a polymorphic type signature in let bindings. Confusingly, I find no such example in the blog post about local let generalization [1]. I first met this problem when porting some test-code from Kiselyov, when refusing to convert its let- bindings into where-bindings (which, btw, can't be typed into GHCi). I've also added a comment to the bug report, but it's still quite possible that I'm missing something.
You don't need ScopedTypeVariables for this, you can just give type signatures in the let binding: Prelude> let id2 :: t -> t; id2 = \x -> x in (id2 '1', id2 10) ('1',10) -- James

On Saturday 21 May 2011 14:55:54, Paolo G. Giarrusso wrote:
However, it turns out that Prelude> let id2 :: Int -> Int = \x -> x works but there's no way whatsoever to make the following work: Prelude> let (id2 :: forall t. t -> t) = \x -> x
Works without problems or extensions in the form Prelude> let id2 :: t -> t; id2 = \x -> x Prelude> :t id2 id2 :: t -> t Also Prelude> let a :: t -> t; a = a a Prelude> :i a a :: t -> t -- Defined at <interactive>:1:18 (of course, using a loops).

I have no problems in defining those functions: $ ghci GHCi, version 6.12.3: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Loading package ffi-1.0 ... linking ... done. Prelude> let id2 :: t -> t; id2 = \x -> x Prelude> let a :: a -> a; a = a a Note that I used ';'. This is equivalent to writing let a :: a -> a a = a a If I try the syntax you were trying without ScopedTypeVariables, I get Prelude> let a :: a -> a = a a <interactive>:1:4: Illegal signature in pattern: a -> a Use -XScopedTypeVariables to permit it GHCi thinks that you were trying to define a function by pattern matches and there was a signature inside it, which is a different thing. This is the expected use for such thing: Prelude> :s -XScopedTypeVariables Prelude> let id2 :: forall t. t -> t; id2 (x :: t) = x Cheers, =) -- Felipe.

On May 21, 3:18 pm, Felipe Almeida Lessa
I have no problems in defining those functions:
$ ghci GHCi, version 6.12.3:http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Loading package ffi-1.0 ... linking ... done. Prelude> let id2 :: t -> t; id2 = \x -> x Prelude> let a :: a -> a; a = a a
Note that I used ';'. This is equivalent to writing
let a :: a -> a a = a a
If I try the syntax you were trying without ScopedTypeVariables, I get
Prelude> let a :: a -> a = a a <interactive>:1:4: Illegal signature in pattern: a -> a Use -XScopedTypeVariables to permit it
GHCi thinks that you were trying to define a function by pattern matches and there was a signature inside it, which is a different thing. As I said, I'm convinced that the argument of let is a pattern, on which a signature is allowed, and GHC correctly understands that, so
First, thanks to you and everybody for the alternative. But I'm still convinced that the syntax is supposed to work, and you're just workarounding the bug. that this declaration work: let (id :: Int -> Int) = \x -> x See both of: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/other-type-extensions... http://www.haskell.org/onlinereport/decls.html#sect4.4.3.2
This is the expected use for such thing:
Prelude> :s -XScopedTypeVariables Prelude> let id2 :: forall t. t -> t; id2 (x :: t) = x
Why does then the following declaration work? let (id :: Int -> Int) = \x -> x To me, this inconsistent behavior is a bug, and surely it is undesirable, because of inconsistency - the programmer needs to have sensible rules, about when let var : type = expr works and when not. Or at least a sensible error message. Moreover, the proposed solution not always works. Neither of these work: let f :: x -> x; g :: y -> y; (f, g) = (id, id) let f :: x -> x; g :: y -> y; (f, g) = (f, g) let (f :: forall a. a -> a, g :: forall a. a -> a) = (f, g) let (f :: forall a. a -> a, g :: forall a. a -> a) = (id, id) If you wonder why one , the tuple might be returned by another function. Additionally, I'd like to debug documentation. If, when looking up docs, I can't find a solution, either I miss something or the documentation is incomplete. In the second case, I'd like to propose fixes. I've just verified that the Haskell Report allows the syntax you describe, but of course it doesn't specify the behavior of the extension. But for instance I've not found type declaration for let bindings in Real World Haskell (a Gentle introduction to Haskell mentions them in one point of the document, without examples). Cheers, Paolo G. Giarrusso

On Sat, May 21, 2011 at 10:17, Paolo G. Giarrusso
First, thanks to you and everybody for the alternative. But I'm still convinced that the syntax is supposed to work, and you're just workarounding the bug.
Hm, I think we have differing expectations; the syntax everyone presented is the one that is normally used for such things in ghci, and while perhaps the one you tried is supposed to work, it's a bit unusual and therefore probably not very well tested.

On Sat, May 21, 2011 at 16:27, Brandon Allbery
On Sat, May 21, 2011 at 10:17, Paolo G. Giarrusso
wrote: First, thanks to you and everybody for the alternative. But I'm still convinced that the syntax is supposed to work, and you're just workarounding the bug.
Hm, I think we have differing expectations; the syntax everyone presented is the one that is normally used for such things in ghci, and while perhaps the one you tried is supposed to work, it's a bit unusual and therefore probably not very well tested.
Yes, it was my guess as well - and for some reason I missed the usual syntax. A sincere question about the usual syntax: where do you learn it? It's needed seldom enough that I don't see it often in tutorials. And Googling for "let type signature haskell" leads me to the Haskell report or to ScopedTypeVariables. Cheers, -- Paolo Giarrusso - Ph.D. Student http://www.informatik.uni-marburg.de/~pgiarrusso/

On Sat, May 21, 2011 at 10:49, Paolo Giarrusso
On Sat, May 21, 2011 at 10:17, Paolo G. Giarrusso
wrote:
First, thanks to you and everybody for the alternative. But I'm still convinced that the syntax is supposed to work, and you're just workarounding the bug.
Hm, I think we have differing expectations; the syntax everyone presented is the one that is normally used for such things in ghci, and while perhaps
one you tried is supposed to work, it's a bit unusual and therefore
On Sat, May 21, 2011 at 16:27, Brandon Allbery
wrote: the probably not very well tested.
Yes, it was my guess as well - and for some reason I missed the usual syntax. A sincere question about the usual syntax: where do you learn it? It's
It's just the desugaring of layout, with the braces being optional because it's all on one line. Since you can't use layout in ghci, you have to manually convert. It is perhaps not entirely obvious (I was a bit surprised when, as a complete Haskell newbie, I decided to try it and it worked) that you can specify types in both let and where clauses in the same way you do at the top level:
let a :: a -> a a = a a where b :: b -> b b = b b
which in its non-layout version is
let { a :: a -> a; a = a a } where { b :: b -> b; b = b b }
(using the braces here because it's a bit ambiguous; I think H'2010 fixed that though)

On Sat, May 21, 2011 at 17:13, Brandon Allbery
On Sat, May 21, 2011 at 10:49, Paolo Giarrusso
wrote: On Sat, May 21, 2011 at 16:27, Brandon Allbery
wrote: On Sat, May 21, 2011 at 10:17, Paolo G. Giarrusso
wrote: First, thanks to you and everybody for the alternative. But I'm still convinced that the syntax is supposed to work, and you're just workarounding the bug.
Hm, I think we have differing expectations; the syntax everyone presented is the one that is normally used for such things in ghci, and while perhaps the one you tried is supposed to work, it's a bit unusual and therefore probably not very well tested.
Yes, it was my guess as well - and for some reason I missed the usual syntax. A sincere question about the usual syntax: where do you learn it? It's
It's just the desugaring of layout, with the braces being optional because it's all on one line. Since you can't use layout in ghci, you have to manually convert.
It is perhaps not entirely obvious (I was a bit surprised when, as a complete Haskell newbie, I decided to try it and it worked) that you can specify types in both let and where clauses in the same way you do at the top level:
Exactly. I've seen examples with where, but only today I've seen this example with let. Luckily I was aware of layout desugaring - that's explained in most tutorials I know.
let a :: a -> a a = a a
Anyway, thanks. I hope some more advanced tutorial, some day, will also show such examples. -- Paolo Giarrusso - Ph.D. Student http://www.informatik.uni-marburg.de/~pgiarrusso/

On Sat, May 21, 2011 at 11:17 AM, Paolo G. Giarrusso
As I said, I'm convinced that the argument of let is a pattern, on which a signature is allowed, and GHC correctly understands that, so that this declaration work: let (id :: Int -> Int) = \x -> x
See both of: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/other-type-extensions... http://www.haskell.org/onlinereport/decls.html#sect4.4.3.2
Actually, it doesn't work ;-) Prelude> let (id2 :: Int -> Int) = \x -> x <interactive>:1:5: Illegal signature in pattern: Int -> Int Use -XScopedTypeVariables to permit it Patterns can't have types in Haskell 98 [1]. So you actually need to see what the ScopedTypeVariables say about it [2, 3]. The GHC user guide [3] is pretty clear saying that pattern type signatures can't bring new type variables into scope like you want. [1] http://www.haskell.org/onlinereport/exps.html#sect3.17.1 [2] http://hackage.haskell.org/trac/haskell-prime/wiki/ScopedTypeVariables [3] http://www.haskell.org/ghc/docs/7.0-latest/html/users_guide/other-type-exten...
To me, this inconsistent behavior is a bug, and surely it is undesirable, because of inconsistency - the programmer needs to have sensible rules, about when let var : type = expr works and when not. Or at least a sensible error message.
The bug seems to be with the error message. But the behaviour looks like what was expressed by the docs. I think you could file a bug about it.
Moreover, the proposed solution not always works. Neither of these work: let f :: x -> x; g :: y -> y; (f, g) = (id, id) let f :: x -> x; g :: y -> y; (f, g) = (f, g)
let (f :: forall a. a -> a, g :: forall a. a -> a) = (f, g) let (f :: forall a. a -> a, g :: forall a. a -> a) = (id, id)
If you wonder why one , the tuple might be returned by another function.
Prelude> let f :: x -> x; g :: y -> y; (f, g) = (id, id) <interactive>:1:40: Couldn't match expected type `forall x. x -> x' against inferred type `a -> a' In the expression: id In the expression: (id, id) In a pattern binding: (f, g) = (id, id) Prelude> :s -XNoMonoPatBinds Prelude> let f :: x -> x; g :: y -> y; (f, g) = (id, id) See [4]. [4] http://hackage.haskell.org/trac/haskell-prime/wiki/MonomorphicPatternBinding... Cheers! =) -- Felipe.

On Sat, May 21, 2011 at 16:46, Felipe Almeida Lessa
On Sat, May 21, 2011 at 11:17 AM, Paolo G. Giarrusso
wrote: As I said, I'm convinced that the argument of let is a pattern, on which a signature is allowed, and GHC correctly understands that, so that this declaration work: let (id :: Int -> Int) = \x -> x
See both of: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/other-type-extensions... http://www.haskell.org/onlinereport/decls.html#sect4.4.3.2
Actually, it doesn't work ;-)
Prelude> let (id2 :: Int -> Int) = \x -> x <interactive>:1:5: Illegal signature in pattern: Int -> Int Use -XScopedTypeVariables to permit it
I tried that with -XScopedTypeVariables enabled, I didn't say it because I thought it was implicit, and for conciseness: $ ghci Prelude> :set -XScopedTypeVariables Prelude> let (id :: Int -> Int) = \x -> x Prelude> id 1 1
Patterns can't have types in Haskell 98 [1]. So you actually need to see what the ScopedTypeVariables say about it [2, 3]. The GHC user guide [3] is pretty clear saying that pattern type signatures can't bring new type variables into scope like you want.
Well, the examples don't use forall. The guide states that: "Unlike expression and declaration type signatures, pattern type signatures are not implicitly generalised." And I don't want implicit generalization. Adding "nor can they be explicitly generalized" would already be unambiguous, and extra examples would be nice. The rest of the text might or might not forbid to explicitly introduce type variables with forall, but it doesn't say that. I agree that your interpretation is also plausible, but then I'd like the text to be clarified (and the error message to be fixed as you suggest).
[1] http://www.haskell.org/onlinereport/exps.html#sect3.17.1 [2] http://hackage.haskell.org/trac/haskell-prime/wiki/ScopedTypeVariables [3] http://www.haskell.org/ghc/docs/7.0-latest/html/users_guide/other-type-exten...
To me, this inconsistent behavior is a bug, and surely it is undesirable, because of inconsistency - the programmer needs to have sensible rules, about when let var : type = expr works and when not. Or at least a sensible error message.
The bug seems to be with the error message. But the behaviour looks like what was expressed by the docs. I think you could file a bug about it.
Moreover, the proposed solution not always works. Neither of these work: let f :: x -> x; g :: y -> y; (f, g) = (id, id) let f :: x -> x; g :: y -> y; (f, g) = (f, g)
let (f :: forall a. a -> a, g :: forall a. a -> a) = (f, g) let (f :: forall a. a -> a, g :: forall a. a -> a) = (id, id)
If you wonder why one , the tuple might be returned by another function.
Prelude> let f :: x -> x; g :: y -> y; (f, g) = (id, id) <interactive>:1:40: Couldn't match expected type `forall x. x -> x' against inferred type `a -> a' In the expression: id In the expression: (id, id) In a pattern binding: (f, g) = (id, id) Prelude> :s -XNoMonoPatBinds Prelude> let f :: x -> x; g :: y -> y; (f, g) = (id, id)
See [4].
[4] http://hackage.haskell.org/trac/haskell-prime/wiki/MonomorphicPatternBinding...
I see, thanks. This applies even to explicit foralls, which are ignored instead of giving an error. So even the following line only works with -XNoMonoPatBinds, and I get no complaints about the foralls: let f :: forall x. x -> x; g :: forall y. y -> y; (f, g) = (id, id) I guess, I should report a bug about that. Cheers, -- Paolo Giarrusso - Ph.D. Student http://www.informatik.uni-marburg.de/~pgiarrusso/

On Saturday 21 May 2011 16:17:53, Paolo G. Giarrusso wrote:
As I said, I'm convinced that the argument of let is a pattern, on which a signature is allowed, and GHC correctly understands that, so that this declaration work: let (id :: Int -> Int) = \x -> x
I don't think that Prelude> let a :: a -> a = a a (or even Prelude> let id2 :: t -> t = \x -> x ) is supposed to work: "The pattern in a pattern binding may only mention type variables that are already in scope." Both are pattern bindings and the mentioned type variable is not in scope.
See both of: http://www.haskell.org/ghc/docs/7.0.3/html/users_guide/other-type-extens ions.html#pattern-type-sigs http://www.haskell.org/onlinereport/decls.html#sect4.4.3.2
This is the expected use for such thing:
Prelude> :s -XScopedTypeVariables Prelude> let id2 :: forall t. t -> t; id2 (x :: t) = x
Why does then the following declaration work? let (id :: Int -> Int) = \x -> x
Because it doesn't mention any type variable.
To me, this inconsistent behavior is a bug, and surely it is undesirable, because of inconsistency - the programmer needs to have sensible rules, about when let var : type = expr works and when not. Or at least a sensible error message.
The error message definitely deserves improvement.
participants (6)
-
Brandon Allbery
-
Daniel Fischer
-
Felipe Almeida Lessa
-
James Cook
-
Paolo G. Giarrusso
-
Paolo Giarrusso