I have been messing around with continuation passing style in Haskell. Taking the Cont
monad apart and removing the type wrappers helped me in understanding the implementation. Here is the code:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad (ap)
newtype Cont r a = Cont {runCont :: (a -> r) -> r}
instance Functor (Cont r) where
fmap f ka = ka >>= pure . f
instance Applicative (Cont r) where
(<*>) = ap
pure = return
instance Monad (Cont r) where
ka >>= kab = Cont kb'
where
-- kb' :: (b -> r) -> r
kb' hb = ka' ha
where
-- ha :: (a -> r)
ha a = (kab' a) hb
-- ka' :: (a -> r) -> r
ka' = runCont ka
-- kab' :: a -> (b -> r) -> r
kab' a = runCont (kab a)
return a = Cont ka'
where
-- ka' :: (a -> r) -> r
ka' ha = ha a
This code compiles (using GHC 8.0.2) and everything seems fine. However, as soon as I uncomment any of the (now commented) type signatures in the where block I get an error. For exapmle, if I uncomment the line
-- ka' :: (a -> r) -> r
I get:
• Couldn't match type ‘a’ with ‘a1’
‘a’ is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b. Cont r a -> (a -> Cont r b) -> Cont r b
at cont.hs:19:6
‘a1’ is a rigid type variable bound by
the type signature for:
ka' :: forall a1. (a1 -> r) -> r
at cont.hs:27:14
Expected type: (a1 -> r) -> r
Actual type: (a -> r) -> r
• In the expression: runCont ka
In an equation for ‘ka'’: ka' = runCont ka
In an equation for ‘>>=’:
ka >>= kab
= Cont kb'
where
kb' hb
= ka' ha
where
ha a = (kab' a) hb
ka' :: (a -> r) -> r
ka' = runCont ka
kab' a = runCont (kab a)
• Relevant bindings include
ka' :: (a1 -> r) -> r (bound at cont.hs:28:7)
kab' :: a -> (b -> r) -> r (bound at cont.hs:31:7)
kab :: a -> Cont r b (bound at cont.hs:19:10)
ka :: Cont r a (bound at cont.hs:19:3)
(>>=) :: Cont r a -> (a -> Cont r b) -> Cont r b
(bound at cont.hs:19:3)
Failed, modules loaded: none.
So I tried using a type wildcard to let the compiler tell me what type signature I should put there. As such I tried the following signature:
ka' :: _
Which gave the following error:
• Found type wildcard ‘_’ standing for ‘(a -> r) -> r’
Where: ‘r’ is a rigid type variable bound by
the instance declaration at cont.hs:15:10
‘a’ is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b. Cont r a -> (a -> Cont r b) -> Cont r b
at cont.hs:19:6
To use the inferred type, enable PartialTypeSignatures
• In the type signature:
ka' :: _
In an equation for ‘>>=’:
ka >>= kab
= Cont kb'
where
kb' hb
= ka' ha
where
ha a = (kab' a) hb
ka' :: _
ka' = runCont ka
kab' a = runCont (kab a)
In the instance declaration for ‘Monad (Cont r)’
• Relevant bindings include
ka' :: (a -> r) -> r (bound at cont.hs:28:7)
kab' :: a -> (b -> r) -> r (bound at cont.hs:31:7)
kab :: a -> Cont r b (bound at cont.hs:19:10)
ka :: Cont r a (bound at cont.hs:19:3)
(>>=) :: Cont r a -> (a -> Cont r b) -> Cont r b
(bound at cont.hs:19:3)
Failed, modules loaded: none.
Now I am really confused, the compiler tells me the type of ka'
is (a -> r) -> r
but as soon as I try to explicitely annotate ka'
with this type,
compiling fails. First I thought I was missing ScopedTypeVariables
but it does not seem to make a difference.
What is going on here?
EDIT
This is similar to the question " Why does this function that uses a scoped type variable in a where clause not typecheck? " in that it requires an explicit forall
to bind type variables. However it is not a duplicate since the answer to this question also requires the InstanceSigs
extension.
a
into scope using aforall
somewhere, but since this is an instance method, I think you also needInstanceSigs
. – Alexis KingScopedTypeVariables
(which putsr
in scope in the instance), but you need to binda
also withInstanceSigs
extension, giving the signature for(>>=)
. You could also avoid the extension by giving(>>=) = bind
and then providing a signature forbind
in awhere
clause. But I rather likeInstanceSigs
and am not sure why they are not normally allowed. – luqui