我正在关注康纳麦克布赖德的“克里斯利幸运箭头”论文,并且我已经发布了我的代码here的实现。简而言之,他定义了以下类型和类:
type a :-> b = forall i . a i -> b i
class IFunctor f where imap :: (a :-> b) -> (f a :-> f b)
class (IFunctor m) => IMonad m where
skip :: a :-> m a
bind :: (a :-> m b) -> (m a :-> m b)
data (a := i) j where
V :: a -> (a := i) i
然后他定义了两种类型的绑定,后者使用(:=)
来限制初始索引:
-- Conor McBride's "demonic bind"
(?>=) :: (IMonad m) => m a i -> (a :-> m b) -> m b i
(?>=) = flip bind
-- Conor McBride's "angelic bind"
(>>=) :: (IMonad m) => m (a := j) i -> (a -> m b j) -> m b i
m >>= f = bind (\(V a) -> f a) m
使用do
和RebindableSyntax
的以下相应定义,后者绑定非常适合重新绑定return
表示法以使用带有fail
扩展名的索引monad:
return :: (IMonad m) => a -> m (a := i) i
return = skip . V
fail :: String -> m a i
fail = error
...但问题是我不能让前绑定(即(?>=)
)起作用。我尝试将(>>=)
和return
定义为:
(>>=) :: (IMonad m) => m a i -> (a :-> m b) -> m b i
(>>=) = (?>=)
return :: (IMonad m) => a :-> m a
return = skip
然后我创建了一个保证存在于特定索引中的数据类型:
data Unit a where
Unit :: Unit ()
但是当我尝试使用do
和(>>=)
的新定义重新绑定return
表示法时,它不起作用,如下例所示:
-- Without do notation
test1 = skip Unit >>= \Unit -> skip Unit
-- With do notation
test2 = do
Unit <- skip Unit
skip Unit
test1
类型检查,但test2
没有,这很奇怪,因为我认为所有RebindableSyntax
所做的都是do
符号desugar test2
到test1
,如果test1
类型检查,那么为什么不test2
?我得到的错误是:
Couldn't match expected type `t0 -> t1'
with actual type `a0 :-> m0 b0'
Expected type: m0 a0 i0 -> (t0 -> t1) -> m Unit ()
Actual type: m0 a0 i0 -> (a0 :-> m0 b0) -> m0 b0 i0
In a stmt of a 'do' block: Unit <- skip Unit
In the expression:
do { Unit <- skip Unit;
skip Unit }
即使我使用显式forall
语法而不是:->
类型的运算符,错误仍然存在。
现在,我知道使用“恶魔绑定”还有另外一个问题,那就是你无法定义(>>)
,但我仍然希望看到它可以走多远。任何人都可以解释为什么我不能让GHC去除“恶魔绑定”,即使它通常会进行类型检查吗?
答案 0 :(得分:9)
IIUC,GHC desugarer实际上在类型检查器(source)之后运行。这就解释了为什么你观察到的情况在理论上是可能的。类型检查器可能有一些特殊的输入规则,这些规则可能与类型检查器对desugarred代码的处理方式不一致。
当然,期望它们保持一致是合理的,所以我建议提交GHC错误。