gpt4 book ai didi

haskell - 在 Haskell 中,为什么即使启用了 AllowAmbiguousTypes 类型也会有歧义?

转载 作者:行者123 更新时间:2023-12-05 00:10:24 25 4
gpt4 key购买 nike

所以让我们假设你有两个这样定义的类型类:

{-# 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 是隐式的。我想把 fg 的实现者留给 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)
| ^^^

最佳答案

您的代码含糊不清,编译器无法自动解决。假设这样:

class F a c where f :: a -> c
class G c b where g :: c -> b

instance F Int String where f = show
instance G String Bool where g = null

h :: (F Int c, G c Bool) => Int -> Bool
h a = g (f a)

现在,最后一行使用了哪些实例?我们有两个选择:使用上下文 (F Int c, G c Bool) 提供的实例,或者忽略该上下文并使用上面的实例,以 String 作为中间类型。两种解释都是正确的,我们确实可以明确地写

h1 :: forall c. (F Int c, G c Bool) => Int -> Bool
h1 a = (g :: c -> Bool) (f a)

h2 :: forall c. (F Int c, G c Bool) => Int -> Bool
h2 a = (g :: String -> Bool) (f a)

选择一种方式或另一种方式。 GHC无法以合理的方式为我们做这个选择。它可以根据一些试探法选择一个,但这会给程序员带来很多惊喜。因此,我们可以争辩说 GHC 绝对不能选择,报告歧义,并让程序员阐明他们的意图。

最后,请注意,您的代码不包含上述两个实例这一事实是无关紧要的,因为它们可以稍后添加,甚至可以添加到另一个模块中,因此 GHC 必须保守并避免假设它们永远不存在。

And why doesn't Haskell complain about this?

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE AllowAmbiguousTypes #-}

class F a c where f :: a -> c
class G c b where g :: c -> b

h x = g (f x) -- [renamed to x for clarity]

好点。这里GHC可以找到一个最通用的类​​型,就是

h :: forall a b c. (F a c, G c b) => a -> b
h x = (g :: c -> b) ((f :: a -> b) x)

由于这里是GHC加入了类型变量c,GHC可以确定这个类型就是中间类型。毕竟,该类型变量是在类型推断期间创建的,用于表示中间类型。

相反,当用户显式写入上下文时,GHC 无法猜测用户的意图。有可能,即使在实践中不太可能,用户不想使用该实例,而是另一个实例(在程序中可用,但不存在于上下文中)。

考虑一下这个案例也可能会有所帮助:

data T = ....

h :: forall a b c. (F a c, G c b, F a T, G T b) => a -> b
h x = g (f x)

我认为您同意应该拒绝此代码:中间类型可能是 Tc,并且没有解决它的明智方法。现在考虑这种情况:

h :: forall a b c. (F a c, G c b) => a -> b
h x = g (f x)

instance F a T where ...
instance G T b where ...

现在,这与之前的情况并没有太大不同。我们没有在上下文中有两个选项,而是将一个选项移到了外面。不过,GHC 仍然有两个选项可供选择。所以,再一次,明智的做法是拒绝代码,并向程序员询问更多细节。


一个更简单的场景,在 GHCi 中:

> :set -XScopedTypeVariables
> :set -XAllowAmbiguousTypes
> class C a where c :: String
> instance C Int where c = "Int"
> instance C Bool where c = "Bool"
> let g :: forall a. C a => String ; g = c

<interactive>:7:40: error:
* Could not deduce (C a0) arising from a use of `c'

在这里,GHC 怎么知道当我写 g = c 时,我的意思是“c 来自上下文 C a?我可以写出意思是“Int 实例中的 c”。或者 Bool

GHC 在内部生成一个新的类型变量 a0,然后尝试解决约束 C a0。它具有三个选择:选择 a0 = aa0 = Inta0 = Bool。 (以后可以添加更多实例!)

所以,它是模棱两可的,如果不猜测程序员的意图,就没有理智的方法来修复它。唯一安全的选择是拒绝。

关于haskell - 在 Haskell 中,为什么即使启用了 AllowAmbiguousTypes 类型也会有歧义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58471799/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com