【发布时间】:2019-10-20 09:49:08
【问题描述】:
假设你有两个这样定义的类型类:
{-# LANGUAGE MultiParamTypeClasses #-}
class F a c where f :: a -> c
class G c b where g :: c -> b
然后你想通过使用 f 和 g 以一般方式定义一个新函数 h。
h a = g (f a)
我们知道这个函数的类型是a -> b,所以c 是隐含的。我想把它留给f 和g 的实现者c 可能是什么。 Haskell 抱怨这句话 c 含糊不清。
然后按照错误的建议,我打开了这个扩展:
{-# LANGUAGE AllowAmbiguousTypes #-}
现在可以了!不错。
我相信通常作为一种良好的软件工程实践,我想为我的函数编写明确的规范,以告诉编译器我期望我的函数应该如何表现。这样以后的编译器就可以抱怨我不尊重我的设置。
所以我想在它之前添加我的函数的类型:
h :: (F a c, G c b) => a -> b
h a = g (f a)
现在类型歧义错误又来了……为什么?
总结一下为什么 Haskell 会抱怨下面这段代码?即使 AllowAmbiguousTypes 被明确启用。如何在保持明确的函数类型定义的同时修复它?我知道删除函数的类型定义可以解决问题,但我不想指定不足。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
class F a c where f :: a -> c
class G c b where g :: c -> b
h :: (F a c, G c b) => a -> b
h a = g (f a)
为什么没有 Haskell 抱怨这个?
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
class F a c where f :: a -> c
class G c b where g :: c -> b
h a = g (f a)
错误信息:
error:
* Could not deduce (G c0 b) arising from a use of `g'
from the context: (F a c, G c b)
bound by the type signature for:
h :: forall a c b. (F a c, G c b) => a -> b
The type variable `c0' is ambiguous
Relevant bindings include
h :: a -> b
* In the expression: g (f a)
In an equation for `h': h a = g (f a)
|
| h a = g (f a)
| ^^^^^^^
error:
* Could not deduce (F a c0) arising from a use of `f'
from the context: (F a c, G c b)
bound by the type signature for:
h :: forall a c b. (F a c, G c b) => a -> b
The type variable `c0' is ambiguous
Relevant bindings include
a :: a
h :: a -> b
* In the first argument of `g', namely `(f a)'
In the expression: g (f a)
In an equation for `h': h a = g (f a)
|
| h a = g (f a)
| ^^^
【问题讨论】:
-
因为无法根据类型签名确定
c应该是什么。所以函数的“用户”,不能决定c。 -
@WillemVanOnsem 但如果您不指定 h :: (F a c, G c b) => a -> b,它会起作用。那么为什么它在一种情况下有效而在另一种情况下无效呢?
-
我不是这些问题的专家,但我认为问题在于 GHC 不知道您要使用哪个
c,启用功能依赖可能会有所帮助。 -
如果启用
-XScopedTypeVariables并将签名更改为h :: forall c b a. (F a c, G c b) => a -> b(实现中可以引用中间类型c),同时启用-XTypeApplications,则可以写h a = g @c (f a)。