我有这段代码:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, KindSignatures, GADTs, FlexibleInstances, FlexibleContexts #-}
class Monad m => Effect p e r m | p e m -> r where
fin :: p e m -> e -> m r
data ErrorEff :: * -> (* -> *) -> * where
CatchError :: (e -> m a) -> ErrorEff ((e -> m a) -> m a) m
instance Monad m => Effect ErrorEff ((e -> m a) -> m a) a m where
fin (CatchError h) = \f -> f h
这不能编译,在最后一行中出现此类型错误:
Could not deduce (a1 ~ a)
from the context (Monad m)
[...]
or from (((e -> m a) -> m a) ~ ((e1 -> m a1) -> m a1))
[...]
如果我将m
更改为[]
它编译得很好,那么显然GHC认为m
不是单射的。 (虽然它没有像类型系列那样警告注入性。)
我的GHC版本是7.2.1。
修改:如果我将(e -> m a)
更改为e
,则可以将其更改为m a
,但不会更改为(m a -> e)
。
答案 0 :(得分:31)
这不是一个错误,但这是一个漫长的故事......
在7.0中曾经有一个名为right
的强制构造函数,其工作方式如下:
g : f a ~ f b
---------------
right g : a ~ b
也就是说,如果g
是f a
和f b
之间的强制,则right g
是a
和b
之间的强制。只有在保证f
是单射的情况下才会发出声音:否则我们可能合法地拥有f Int ~ f Char
,然后我们就可以得出结论Int ~ Char
,这将是不好的。
但是,当然,类型同义词和类型族不一定是单射的;例如:
type T a = Int
type family F a :: *
type instance F Int = Bool
type instance F Char = Bool
那么这种保证怎么可能呢?嗯,这正是为什么不允许部分应用类型同义词和类型系列的原因。类型同义词和类型族的部分应用可能不是单射的,但饱和的应用(甚至是导致更高类型的应用)总是如此。
当然,对部分应用程序的限制令人讨厌。所以在7.2中,为了向允许部分应用的方向移动(并且因为它简化了强制语言的理论和实现),right
构造函数被构造函数nth
替换,随附的规则
g : T a1 .. an ~ T b1 .. bn
---------------------------
nth i g : ai ~ bi
也就是说,nth
仅适用于两种类型之间的强制g
,这两种类型已知是类型构造函数T
的饱和应用程序。理论上,这允许类型同义词和族的部分应用,因为我们不能分解等式,直到我们知道它们在(必然是单射的)类型构造函数的应用程序之间。特别是,nth
不适用于强制f a ~ f b
,因为f
是类型变量,而不是类型构造函数。
有人认为在改变时没有人会注意到,但显然这是错误的!
有趣的是,haskell-cafe message from Daniel Schüssler中概述的Olegian技巧表明,类型系列的实现没有相应改变!问题是像
这样的定义type family Arg fa
type instance Arg (f a) = a
如果f
可能是非单射的,则不应该允许;在这种情况下,定义甚至没有意义。
我认为正确的做法是恢复right
(或类似的东西),因为人们显然想要它!希望这很快就会完成。
与此同时,允许部分应用的类型同义词和系列仍然非常酷。这样做的正确方法(tm)似乎是跟踪类型系统中的注入性:也就是说,每种箭头类型都会以其注入性进行注释。这种方式遇到等式f a ~ f b
时,我们可以查看f
的类型,以确定将其分解为等式a ~ b
是否安全。并非巧合,我目前正在努力设计出这样一个系统。 =)
答案 1 :(得分:2)
我不确定原因,但我将您的测试用例缩减为:
{-# LANGUAGE GADTs #-}
data ErrorEff x where
CatchError :: m a -> ErrorEff (m a)
fin :: ErrorEff (m a) -> m a
fin (CatchError h) = h
编译在GHC 7.0.3中但不是7.3.20111021。
这绝对是一个编译错误。
更改后编译:
data ErrorEff x where
CatchError :: x -> ErrorEff x
可以使用记录语法恢复函数“fin”:
data ErrorEff x where
CatchError :: { fin :: m a } -> ErrorEff (m a)