so i was playing around with Extensible effects trying to reimplement some of the fundamental types and functions when i got this type error :
home/kadhem/projects/Haskell/stackWorkingAgain/app/Implementation_error.hs:24:96: error:
* Couldn't match type `x1' with `x'
`x1' is a rigid type variable bound by
a pattern with constructor:
Impure :: forall (r :: [* -> *]) a x.
Union r x -> (x -> Eff r a) -> Eff r a,
in an equation for `createInterpreter'
at /home/kadhem/projects/Haskell/stackWorkingAgain/app/Implementation_error.hs:22:59-68
`x' is a rigid type variable bound by
the type signature for:
createInterpreter :: forall a (r :: [* -> *]) b (f :: * -> *) x.
(a -> Eff r b)
-> (f x -> (x -> Eff (f : r) a) -> Eff (f : r) a)
-> (Eff r b -> Eff r b)
-> Eff (f : r) a
-> Eff r b
at /home/kadhem/projects/Haskell/stackWorkingAgain/app/Implementation_error.hs:(15,1)-(18,47)
Expected type: f x
Actual type: f x1
* In the first argument of `runContinuation', namely
`f_is_theTarget'
i don't understand how is x1 bound by the constructor Impure.
here is the full code :
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GADTSyntax #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
module Implemntation where
import Data.OpenUnion (Union , decomp)
data Eff r a where
Pure :: a -> Eff r a
Impure :: Union r x -> (x -> Eff r a) -> Eff r a
createInterpreter :: (a -> Eff r b)
-> (f x -> (x -> Eff (f ': r) a) -> Eff (f ': r) a)
-> (Eff r b -> Eff r b)
-> Eff (f ': r) a -> Eff r b
createInterpreter handlePure runContinuation applyEffect (Pure a) =
handlePure a
createInterpreter handlePure runContinuation applyEffect (Impure f k)
= case decomp f of
Right f_is_theTarget
-> applyEffect $ createInterpreter handlePure runContinuation
applyEffect (runContinuation f_is_theTarget k)
Left f_is_notTheTarget
-> Impure f_is_notTheTarget $ createInterpreter handlePure
runContinuation applyEffect . k
Consider these two facts:
Fact 1: whoever calls Impure
gets to choose the type of x
in its type:
Impure :: Union r x -> (x -> Eff r a) -> Eff r a
Note that such x
does not end up in Eff ra
, making the type an "existential" one.
Fact 2: whoever calls createInterpreter
gets to choose the value of x
in its type
createInterpreter :: (a -> Eff r b)
-> (f x -> (x -> Eff (f ': r) a) -> Eff (f ': r) a)
-> (Eff r b -> Eff r b)
-> Eff (f ': r) a -> Eff r b
Note that this choice is independent from the first choice!
So, both these facts imply that when we call
createInterpreter handlePure runContinuation applyEffect (Impure f k)
we can pass a runContinuation
function for a type x
and pass Impure
using a distinct type x
(say, x1
). Since you are trying to mix these types, the compiler complains that they do not have to be the same.
You might want the following type instead
createInterpreter :: (a -> Eff r b)
-> (forall x. f x -> (x -> Eff (f ': r) a) -> Eff (f ': r) a)
-> (Eff r b -> Eff r b)
-> Eff (f ': r) a -> Eff r b
This will force you later on to pass a polymorphic runContinuation
function, which works on any choice of x
which might be found "inside" Impure
.
Your example is quite complex, but to understand the issue you can also consider this similar case:
data SomeList where
SL :: [x] -> SomeList
sumIntList :: [Int] -> Int
sumIntList = sum
workWithSL :: ([x] -> Int) -> SomeList -> Int
workWithSL f (SL list) = f list
test :: Int
test = workWithSL sumIntList (SL [True, False])
Here, we choose x = Bool
when calling SL
, but we choose x = Int
when calling workWithSL
(since we pass sumIntList
). There is nothing wrong with such calls, since the types perfectly check out. The real issue is inside workWithSL
which can not apply f
to list
, since the list
may be of type [x1]
instead of [x]
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.