Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Haskell中,为什么即使启用了AllowAmbigUstypes,类型也不明确?_Haskell_Typeclass - Fatal编程技术网

在Haskell中,为什么即使启用了AllowAmbigUstypes,类型也不明确?

在Haskell中,为什么即使启用了AllowAmbigUstypes,类型也不明确?,haskell,typeclass,Haskell,Typeclass,假设有两个类型类定义如下: {-#语言多段类型类} 类别F a c,其中F::a->c 类别G c b,其中G::c->b 然后你想用f和g,用一般的方法定义一个新的函数h ha=g(fa) 我们知道这个函数的类型是a->b,所以c是隐式的。我想把它留给f和g的实现者c可能是什么。哈斯克尔抱怨说,c这句话模棱两可 然后,根据错误提示,我打开了此扩展: {-#语言允许使用模糊类型} 现在它工作了!很好 我相信,通常作为一种良好的软件工程实践,我希望编写函数的显式规范,告诉编译器我期望函数的行

假设有两个类型类定义如下:

{-#语言多段类型类}
类别F a c,其中F::a->c
类别G c b,其中G::c->b
然后你想用f和g,用一般的方法定义一个新的函数h

ha=g(fa)
我们知道这个函数的类型是
a->b
,所以c是隐式的。我想把它留给
f
g
的实现者
c
可能是什么。哈斯克尔抱怨说,
c
这句话模棱两可

然后,根据错误提示,我打开了此扩展:

{-#语言允许使用模糊类型}
现在它工作了!很好

我相信,通常作为一种良好的软件工程实践,我希望编写函数的显式规范,告诉编译器我期望函数的行为。这样以后的编译器可以抱怨我不尊重我设置的内容

因此,我想在前面添加函数的类型:

h::(fac,gcb)=>a->b
h a=g(f a)
现在类型模糊性错误再次出现。。。为什么?

总结一下为什么Haskell会抱怨下面这段代码?即使AllowAmbigUstypes已明确启用。如何在保留显式函数类型定义的同时修复它?我知道删除函数的类型定义可以解决这个问题,但我不想把事情说得太具体

{-#语言多段类型类}
{-#语言允许歧义类型}
类别F a c,其中F::a->c
类别G c b,其中G::c->b
h::(fac,gcb)=>a->b
h a=g(f a)
为什么哈斯克尔不抱怨这一点

{-# 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]
{-#语言多段类型类}
{-#语言允许歧义类型}
类别F a c,其中F::a->c
类别G c b,其中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)
提供的实例,或者忽略该上下文并使用上面的实例,将
字符串
作为中间类型。这两种解释都是正确的,事实上,我们可以明确地写出来

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必须保守,避免假设它们永远不存在

哈斯克尔为什么不抱怨呢

{-# 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)
我想你可以同意这个代码应该被拒绝:中间类型可以是
T
c
,没有合理的方法来解决它。现在考虑这种情况:

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'
:set-XScopedTypeVariables
>:set-XAllowAmbiguousTypes
>类C,其中C::String
>实例C Int,其中C=“Int”
>实例C Bool,其中C=“Bool”
>让g::对于所有a。cA=>字符串;g=c
:7:40:错误:
*无法推断因使用“C”而产生的(cA0)
在这里,GHC怎么知道当我写
g=c
时,我的意思是“来自上下文
ca
c
?我本可以写这句话,意思是“来自
Int
实例的
c
”。或者
Bool

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


因此,它是模棱两可的,在不猜测程序员意图的情况下,没有合理的方法来修复它。唯一安全的选择是拒绝。

因为不能根据类型签名确定
c
应该是什么。因此“用户”“的函数,不能决定
c
@WillemVanOnsem,但如果不指定h::(fac,gcb)=>a->b,则该函数有效。”。那么,为什么它在一种情况下有效而在另一种情况下无效呢?我不是这些方面的专家,但我相信问题是因为GHC不知道您要使用哪个
c
,启用函数依赖关系可能会有所帮助。如果您启用
-XScopedTypeVariables
,并将签名更改为
h::for all c b a。(F a c,G c b)=>a->b
(能够在实现中引用中间类型
c
),并启用
-XTypeApplications
,然后您可以编写
ha=G@c(F a)
。我不确定是否理解您的答案。我也在学习哈斯克尔。为什么会是d