Suppose we want to write a function ifE
that takes a boolean, two handler functions (producing results of different types but implementing the same type class c
), and some postprocessing function that can handle arbitrary c
's. In other words, we want to generalize this function over Show
:
ifShow :: (Show b1, Show b2)
=> Bool
-> (a -> b1)
-> (a -> b2)
-> a
-> (forall v. Show v => v -> res)
-> res
ifShow b f1 f2 arg postproc = if b then postproc (f1 arg) else postproc (f2 arg)
I've heard there's ConstraintKinds
, so let's turn that on and write this:
{-# LANGUAGE RankNTypes, ConstraintKinds, KindSignatures #-}
import Data.Kind
ifE :: forall (c :: Type -> Constraint) a b1 b2 res
. (c b1, c b2)
=> Bool
-> (a -> b1)
-> (a -> b2)
-> a
-> (forall v. c v => v -> res)
-> res
ifE b f1 f2 arg postproc = if b then postproc (f1 arg) else postproc (f2 arg)
Now, GHC (8.10.4 in my case) complains about the type signature itself:
Main.hs:5:8: error:
• Could not deduce: (c0 b1, c0 b2)
from the context: (c b1, c b2)
bound by the type signature for:
ifE :: forall (c :: * -> Constraint) a b1 b2 res.
(c b1, c b2) =>
Bool
-> (a -> b1)
-> (a -> b2)
-> a
-> (forall v. c v => v -> res)
-> res
at Main.hs:(5,8)-(12,10)
• In the ambiguity check for ‘ifE’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature:
ifE :: forall (c :: Type -> Constraint) a b1 b2 res.
(c b1, c b2) =>
Bool
-> (a -> b1)
-> (a -> b2) -> a -> (forall v. c v => v -> res) -> res
|
5 | ifE :: forall (c :: Type -> Constraint) a b1 b2 res
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
Main.hs:5:8: error:
• Could not deduce: c v
from the context: (c b1, c b2)
bound by the type signature for:
ifE :: forall (c :: * -> Constraint) a b1 b2 res.
(c b1, c b2) =>
Bool
-> (a -> b1)
-> (a -> b2)
-> a
-> (forall v. c v => v -> res)
-> res
at Main.hs:(5,8)-(12,10)
or from: c0 v
bound by a type expected by the context:
forall v. c0 v => v -> res
at Main.hs:(5,8)-(12,10)
• In the ambiguity check for ‘ifE’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature:
ifE :: forall (c :: Type -> Constraint) a b1 b2 res.
(c b1, c b2) =>
Bool
-> (a -> b1)
-> (a -> b2) -> a -> (forall v. c v => v -> res) -> res
|
5 | ifE :: forall (c :: Type -> Constraint) a b1 b2 res
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
I understand that GHC might have trouble figuring out what c
should be (although I'm not sure why it would — can't it do that by unifying c
with the type of postproc
at the call site?), but the error is telling something slightly different. Namely, for some reason another type variable c0
is introduced, although I would expect c
to be bound by the outer forall
. What's going on, and where did I mess up?
a -> b1
and ana -> b2
and ana
. Just pass theb1
and theb2
that you get from applying these functions. (Remember laziness.) – leftaroundabout