我正在使用使用存在量化的ConduitT风格的monad,并且正在玩“ hack鼠”,试图使类型统一。此方案有两个版本:
在这里,Await i
有一个存在量化的a
,它允许await
方法传递它想要的任何类型的i -> Await i -> a
。
{-# LANGUAGE RankNTypes #-}
newtype Piped r a = Piped { unPiped :: forall b. r -> (r -> a -> b) -> b }
instance Functor (Piped r) where
fmap f (Piped c) = Piped $ \r rest -> c r (\r -> rest r . f)
runPiped :: Piped r a -> r -> a
runPiped (Piped p) r = p r $ \_ -> id
newtype Await i = Await { unAwait :: forall a. Yield i a -> a }
newtype Yield i a = Yield { unYield :: i -> Await i -> a }
runAwait :: Await i -> (i -> Await i -> a) -> a
runAwait (Await await) = await . Yield
runYield :: Yield i a -> i -> (Yield i a -> a) -> a
runYield (Yield yield) i = yield i . Await
-- broke ^^^^^
-- because Await swallows the type of `a`
await :: forall i a y. Piped (Await i, y) i
await = Piped $
\(a, y) f -> runAwait a $ \i a' -> f (a', y) i
失败:
• Couldn't match type ‘Yield i a -> a’
with ‘forall a1. Yield i a1 -> a1’
Expected type: (Yield i a -> a) -> Await i
Actual type: (forall a. Yield i a -> a) -> Await i
• In the second argument of ‘(.)’, namely ‘Await’
runYield
方法被破坏是因为它无法将Await i
中的现有限定类型参数与a
统一。
第二种情况:
为了修复runYield
,Await
现在指定a
,它将Await i a
与Yield i a
统一起来。但是,既然指定了a
,yield
就无法通过它满意的任何值Yield i b
的{{1}}:
b
失败:
newtype Piped r a = Piped { unPiped :: forall b. r -> (r -> a -> b) -> b }
newtype Await i a = Await { unAwait :: Yield i a -> a }
newtype Yield i a = Yield { unYield :: i -> Await i a -> a }
runAwait :: Await i a -> (i -> Await i a -> a) -> a
runAwait (Await await) = await . Yield
runYield :: Yield i a -> i -> (Yield i a -> a) -> a
runYield (Yield yield) i = yield i . Await
await :: Piped (Await i a, y) i
await = Piped $
\(a, y) f -> runAwait a $ \i a' -> f (a', y) i
-- ^^^^^^
所以我似乎同时需要这两种方式,有时以生存方式量化,而其他时候则是具体的。我尝试创建包装器来隐藏额外的类型参数,将• Couldn't match type ‘b’ with ‘a’
‘b’ is a rigid type variable bound by
a type expected by the context:
forall b. (Await i a, y) -> ((Await i a, y) -> i -> b) -> b
Expected type: (Await i a, y)
Actual type: (Await i b, y)
切换为newtype
,并且还想到如果我可以定义一个函数data
来解决问题,但我在这里有点儿不了解。有什么想法吗?
答案 0 :(得分:0)
尝试定义yield
函数后,我遇到了同样的问题;我不得不从yield
中删除多余的类型变量,并且不会进行类型检查。
解决方案原来是重新定义类型,如下所示:
newtype Await i = Await { unAwait :: forall b. Yield' i b -> b }
newtype Yield i = Yield { unYield :: forall b. i -> Await' i b -> b }
type Await' i a = Yield i -> a
type Yield' i a = i -> Await i -> a
这样,await
中没有其他构造函数可以吞下类型变量:
await :: Piped (Await i, y) i
await = Piped $
\(Await a, y) f -> a $ \i a' -> f (a', y) i