I'm trying to understand singletons. As an exercise I'm manually defining an instance of SingKind
for a custom List
type:
data List a = Nil | Cons a (List a)
data SList :: List a -> Type where
SNil :: SList 'Nil
SCons :: Sing x -> SList xs -> SList ('Cons x xs)
type instance Sing = SList
instance SingKind k => SingKind (List k) where
type Demote (List k) = List (Demote k)
fromSing :: Sing (a :: List k) -> List (Demote k)
toSing :: List (Demote k) -> SomeSing (List k)
This definition of toSing
works fine if I pattern-match x
and xs
via a case-of:
toSing (Cons x xs) = case toSing x of
SomeSing singX -> case toSing xs of
SomeSing singXs -> SomeSing $ SCons singX singXs
But it fails if I pattern match via let:
toSing (Cons x xs) =
let SomeSing singX = toSing x
SomeSing singXs = toSing xs
in SomeSing $ SCons singX singXs
This is the error (another similar one is shown for the following line):
error:
• Couldn't match type ‘x0’ with ‘a’
Expected: Sing @k x0
Actual: Sing @k1 a
• because type variable ‘a’ would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor:
SomeSing :: forall k (a :: k). Sing a -> SomeSing k,
in a pattern binding
at door.hs:169:13-26
• In the pattern: SomeSing singX
In a pattern binding: SomeSing singX = toSing x
In the expression:
let
SomeSing singX = toSing x
SomeSing singXs = toSing xs
in SomeSing $ SCons singX singXs
• Relevant bindings include
singXs :: SList xs0 (bound at door.hs:170:22)
xs :: List (Demote k1) (bound at door.hs:168:20)
x :: Demote k1 (bound at door.hs:168:18)
toSing :: List (Demote k1) -> SomeSing (List k1)
(bound at door.hs:167:5)
|
| let SomeSing singX = toSing x
| ^^^^^
Can you explain why let bindings behave differently from a case-of and in this case they fail?