Can someone please explain to me why this code doesn't work:
import GHC.Exts (build)
data List a = Nil | Cons a (List a)
toLambdaList :: List t0 -> (t0 -> t1 -> t1) -> t1 -> t1
toLambdaList Nil cons nil = nil
toLambdaList (x `Cons` xs) cons nil = x `cons` (toLambdaList xs cons nil)
fromList :: List a -> [a]
fromList = build . toLambdaList
the error I'm getting is this:
Couldn't match type: forall b. (a -> b -> b) -> b -> b
with: (t1 -> t20 -> t20) -> t20 -> t20
(GHCi version 9.0.2)
Suprisingly, if I just replace build . toLambdaList
with \a -> build $ toLambdaList a
, the new code suddenly works:
import GHC.Exts (build)
data List a = Nil | Cons a (List a)
toLambdaList :: List t0 -> (t0 -> t1 -> t1) -> t1 -> t1
toLambdaList Nil cons nil = nil
toLambdaList (x `Cons` xs) cons nil = x `cons` (toLambdaList xs cons nil)
fromList :: List a -> [a]
fromList a = build $ toLambdaList a
I've tried deleting type declarations, but this doesn't help.
The reason why this doesn't work (since GHC 9) is that forall
is treated more literally now in types. In your case the types
In your case the compiler complains that it cannot match these types:
Expected: ((a -> t1 -> t1) -> t1 -> t1) -> [a]
Actual: (forall b. (a -> b -> b) -> b -> b) -> [a]
They do look different, but you may expect that GHC would just instantiate that forall b
in the actual type to t1
. Then the types would match. That is what GHC used to do before version 9 and can now be re-enabled with a new extension called DeepSubsumption
in GHC 9.2 and later.
GHC does still instantiate forall
'd variables in some cases, but not when it is below the left side of a function arrow such as in your case.
The main reasons for making this change were:
For more details see the Simplify Subsumption proposal.
One way you can solve this is by using ImpredicativeTypes
(which can be a replacement for deep subsumption in some cases) and changing the type of toLambdaList
a little:
{-# LANGUAGE ImpredicativeTypes #-}
import GHC.Exts (build)
data List a = Nil | Cons a (List a)
toLambdaList :: List t0 -> forall t1. (t0 -> t1 -> t1) -> t1 -> t1
toLambdaList Nil cons nil = nil
toLambdaList (x `Cons` xs) cons nil = x `cons` (toLambdaList xs cons nil)
fromList :: List a -> [a]
fromList = build . toLambdaList
To understand why this works we need to look at the type of (.)
:
(.) :: forall a b c. (b -> c) -> (a -> b) -> a -> c
Using ImpredicativeTypes
we can instantiate b
to be forall t1. (t0 -> t1 -> t1) -> t1 -> t1
. And a
and b
are instantiated as usual to List a
and [a]
respectively. The specialised type of (.)
then becomes:
(.) :: forall a t0.
((forall t1. (t0 -> t1 -> t1) -> t1 -> t1) -> [a]) ->
(List a -> forall t1. (t0 -> t1 -> t1) -> t1 -> t1) ->
List a -> [a]
Now the type of the first argument matches the type of build
and the type of the second argument matches the type of toLambdaList
.