Haskell FunctionalDependencies不统一于唯一标识的类型
以下是Haskell FunctionalDependencies不统一于唯一标识的类型,haskell,functional-dependencies,type-families,Haskell,Functional Dependencies,Type Families,以下是MonadState的定义,但问题适用于具有功能依赖性的任何此类类: class Monad m=>MonadState s m | m->s where ... 假设我有一个使用s作为类型参数的数据类型和一个使用它的类型类: data StateType s=StateType 类MonadState s m=>FunDeps s m a其中 workWithStateType::a->StateType s->m() 我可以很高兴地为这个类创建一个实例,它可以按照预期编译和工作:
MonadState
的定义,但问题适用于具有功能依赖性的任何此类类:
class Monad m=>MonadState s m | m->s where
...
假设我有一个使用s
作为类型参数的数据类型和一个使用它的类型类:
data StateType s=StateType
类MonadState s m=>FunDeps s m a其中
workWithStateType::a->StateType s->m()
我可以很高兴地为这个类创建一个实例,它可以按照预期编译和工作:
实例(MonadIO m,MonadState s m)=>FunDeps s s m(IORef(StateType s)),其中
workWithStateType ref a=liftIO$writeIORef ref a
但我觉得FunDeps
类中的s
是多余的,我可以这样定义一个类:
class-fundepsma在哪里
workWithStateTypeNoCompile::MonadState s m=>a->StateType s->m()
实例(MonadIO m,MonadState s m)=>fundeps问题m(IORef(StateType s)),其中
...
问题是当我尝试实施它时:
instance (MonadIO m, MonadState s m) => FunDepsProblem m (IORef (StateType s)) where
workWithStateTypeNoCompile ref a = liftIO $ writeIORef ref a
我得到一个编译错误,它告诉我它不能统一实例头和函数中的状态标记s
:
fun-deps.hs:18:62: error: …
• Couldn't match type ‘s1’ with ‘s’
‘s1’ is a rigid type variable bound by
the type signature for:
workWithStateTypeNoCompile :: forall s1.
MonadState s1 m =>
IORef (StateType s) -> StateType s1 -> m ()
at /path/to/fun-deps.hs:18:3-28
‘s’ is a rigid type variable bound by
the instance declaration
at /path/to/fun-deps.hs:17:10-78
Expected type: StateType s
Actual type: StateType s1
• In the second argument of ‘writeIORef’, namely ‘a’
In the second argument of ‘($)’, namely ‘writeIORef ref a’
In the expression: liftIO $ writeIORef ref a
• Relevant bindings include
a :: StateType s1
(bound at /path/to/fun-deps.hs:18:34)
ref :: IORef (StateType s)
(bound at /path/to/fun-deps.hs:18:30)
workWithStateTypeNoCompile :: IORef (StateType s)
-> StateType s1 -> m ()
(bound at /path/to/fun-deps.hs:18:3)
|
Compilation failed.
我理解,当它以这种形式定义时,所有
都有一个隐含的:
workWithStateTypeNoCompile :: forall s m a . MonadState s m => a -> StateType s -> m ()
所以从技术上讲,它应该适用于每一个s
,在没有功能依赖性的情况下,它是完全有意义的,但是s
在m
已知时是已知的,所以这是我没有得到的部分
换句话说,monadm
在类头和函数中是统一的,因此它应该唯一地标识实例头和函数类型中的状态类型s
。所以我的问题是为什么它没有统一?这是否有理论上的原因,或者根本没有在ghc中实施
事实上,如果我将MonadState
重写为概念上相同的功能,但使用TypeFamilies
而不是FunctionalDependencies
则问题似乎消失了:
class Monad m=>monadstate族m其中
键入StateToken m::*
班级家庭m a在哪里
familyStateType::MonadStateFamily m=>a->StateType(StateToken m)->m()
实例(MonadIO m,MonadStateFamily m,s~StateToken m)=>Family m(IORef(StateType s)),其中
familyStateType ref a=liftIO$writeIORef ref a
显然,这是功能相关性的已知限制。我在十多年前挖了一家Haskell咖啡馆,其中提到功能依赖性
不适用于存在类型,并提供了一个非常简洁明了的示例:
类别F a r | a->r
实例F Bool Int
数据T a=全部b。fab=>mktb
添加::T Bool->T Bool->T Bool
加上(MkT x)(MkT y)=MkT(x+y)
上面的例子产生了一个编译器错误,它说它不能统一到唯一标识的类型上,本质上就是问题的标题
• Couldn't match expected type ‘b’ with actual type ‘b1’
‘b1’ is a rigid type variable bound by
a pattern with constructor: MkT :: forall a b. F a b => b -> T a,
in an equation for ‘add’
这是问题中的编译错误,看起来与上面的问题非常相似
• Couldn't match type ‘s1’ with ‘s’
‘s1’ is a rigid type variable bound by
the type signature for:
workWithStateTypeNoCompile :: forall s1.
MonadState s1 m =>
IORef (StateType s) -> StateType s1 -> m ()
我怀疑这里使用的是完全相同的概念,因为workWithStateTypeNoCompile
上的forall
,错误中的类型变量s1
是存在变量
在任何情况下,并不是所有的东西都丢失了,对于我遇到的问题,有一个体面的解决办法。尤其需要从类实例头中删除s
,这可以通过newtype
实现:
class-fundepsma在哪里
WorkWithStateTypeComile::MonadState s m=>a s->StateType s->m()
newtype StateTypeRef s=StateTypeRef(IORef(StateType s))
实例MonadIO m=>FundepWorks m StateTypeRef,其中
workWithStateTypeCompile(StateTypeRef)a=liftIO$writeIORef ref a
请注意,a
现在是一个arity为1的类型变量,并应用于s
多亏了Ben Gamari编译了wiki页面,否则我将永远不会发现存在类型的例子。思考:当您启用-XScopedTypeVariables并给workWithStateTypeNoCompile
一个显式统一变量的签名时会发生什么?@bradrn值得一试,但运气不佳,同样的错误。我相信GHC只是没有把函数依赖性
视为“函数”。也就是说,没有与之相关的新类型公理。即使你知道classcab | a->b;实例C A B;实例C A C
,没有证据证明B~C
。函数依赖关系实际上是“推理”:如果需要实例ca\u b
而\u b
未知,那么可以选择一个实例(不连贯地)将\u b
设置为一个类型,这是由对实例编写的限制(即b
和C
无论如何必须相同)证明的,但这种平等性并没有作为系统本身的一项规则内在化。@HTNW但FunDeps确保这样的实例不能共存:实例C a B;实例C A C
。这样的实例也是非法的:实例C A\u b
并且任何扩展都不能让您不连贯地拾取它。所以我不太明白你的论点,那是Haskell类型系统的一个真实属性,但是在Haskell类型系统中,根本没有办法证明这一点。例如,excl::或者a(a->Void)
也不可能在(全部)Haskell中写入,但是没有反经典::(对于所有a.或者a(a->Void))->Void
。也就是说,有些事情不是真的,但也不是假的。在这种情况下,B
和C
必须是相同的才能通过实例,但在Haskell内部证明这一点是不正确的。