Skip to content

Commit ef9b718

Browse files
authored
Binding a GADT constr in let doesn't work (#548)
1 parent 4ed7157 commit ef9b718

File tree

8 files changed

+119
-0
lines changed

8 files changed

+119
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module Main where
2+
3+
data Showable where
4+
MkShowable :: Show a => a -> Showable
5+
6+
showShowable :: Showable -> String
7+
showShowable showable =
8+
case showable of
9+
MkShowable x -> show x
10+
11+
main :: IO ()
12+
main = putStrLn $ showShowable (MkShowable 42)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module Main where
2+
3+
data Showable where
4+
MkShowable :: Show a => a -> Showable
5+
6+
showShowable :: Showable -> String
7+
showShowable showable =
8+
let MkShowable x = showable
9+
in show x
10+
11+
main :: IO ()
12+
main = putStrLn $ showShowable (MkShowable 42)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: Binding a `GADT` constructor in `let`
3+
---
4+
5+
In this example, we use a `let` binding to unpack the constructor of a GADT. Naively, this should work fine, because there is only one constructor. Yet GHC does not accept this code!
6+
7+
The fix is to use pattern-matching, either with a `case` or by pattern-matching in a function argument.
8+
9+
For more details about why this is necessary, see the [GHC user guide on ExistentialQuantification](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/existential_quantification.html#restrictions).
10+
11+
Note: if the `TypeFamilies` extension is active, [GHC-46956](/messages/GHC-46956) is generated instead.
12+
13+
## Message
14+
15+
```
16+
Let.hs:8:18: error: [GHC-25897]
17+
• Couldn't match expected type ‘p’ with actual type ‘a’
18+
‘a’ is a rigid type variable bound by
19+
a pattern with constructor:
20+
MkShowable :: forall a. Show a => a -> Showable,
21+
in a pattern binding
22+
at Let.hs:8:7-18
23+
‘p’ is a rigid type variable bound by
24+
the inferred type of x :: p
25+
at Let.hs:8:7-29
26+
• In the pattern: MkShowable x
27+
In a pattern binding: MkShowable x = showable
28+
In the expression: let MkShowable x = showable in show x
29+
|
30+
8 | let MkShowable x = showable
31+
| ^
32+
```

message-index/messages/GHC-25897/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ signature, or by type inference due to the context in which the pattern match oc
1111

1212
To solve the problem you must somehow tell GHC the type of the pattern
1313
match.
14+
15+
A related situation where this error can arise is when binding a GADT constructor with
16+
a `let` binding; see the second example.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{-# LANGUAGE TypeFamilies #-}
2+
module Main where
3+
4+
data Showable where
5+
MkShowable :: Show a => a -> Showable
6+
7+
showShowable :: Showable -> String
8+
showShowable showable =
9+
case showable of
10+
MkShowable x -> show x
11+
12+
main :: IO ()
13+
main = putStrLn $ showShowable (MkShowable 42)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{-# LANGUAGE TypeFamilies #-}
2+
module Main where
3+
4+
data Showable where
5+
MkShowable :: Show a => a -> Showable
6+
7+
showShowable :: Showable -> String
8+
showShowable showable =
9+
let MkShowable x = showable
10+
in show x
11+
12+
main :: IO ()
13+
main = putStrLn $ showShowable (MkShowable 42)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
title: Binding a `GADT` constructor in `let`
3+
---
4+
5+
In this example, we use a `let` binding to unpack the constructor of a GADT. Naively, this should work fine, because there is only one constructor. Yet GHC does not accept this code!
6+
7+
The fix is to use pattern-matching, either with a `case` or by pattern-matching in a function argument.
8+
9+
For more details about why this is necessary, see the [GHC user guide on ExistentialQuantification](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/existential_quantification.html#restrictions).
10+
11+
Note: this example generates GHC-46956 because the `TypeFamilies` extension is active. If it isn't, [GHC-25897](/messages/GHC-25897) is generated instead.
12+
13+
## Message
14+
15+
```
16+
Let.hs:9:18: error: [GHC-46956]
17+
• Couldn't match expected type ‘a0’ with actual type ‘a’
18+
because type variable ‘a’ would escape its scope
19+
This (rigid, skolem) type variable is bound by
20+
a pattern with constructor:
21+
MkShowable :: forall a. Show a => a -> Showable,
22+
in a pattern binding
23+
at Let.hs:9:7-18
24+
• In the pattern: MkShowable x
25+
In a pattern binding: MkShowable x = showable
26+
In the expression: let MkShowable x = showable in show x
27+
|
28+
9 | let MkShowable x = showable
29+
| ^
30+
```

message-index/messages/GHC-46956/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ severity: error
66
---
77

88
This error occurs during kind inference. When inferring a kind for a type variable, GHC creates a fresh metavariable to stand for the kind. Later, if something forces this kind metavariable to be equal to some other kind, unification equates them. However, local kind quantification can lead to the existence of kinds that are only valid in the scope of the quantifier. If a kind metavariable that originated outside this scope were unified with the locally-bound kind, then the resulting program would contain an ill-scoped kind signature.
9+
10+
This situation can arise for multiple reasons.
11+
- In the first example, the cause is a manually-specified type signature with the kind variable in the wrong position.
12+
- In the second example, the cause is a pattern match on a GADT constructor in a `let` binding (use `case` instead).

0 commit comments

Comments
 (0)