简体   繁体   中英

GHC type error doesn't make sense to me

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM