Haskell 为什么';这不是型式检验吗?

Haskell 为什么';这不是型式检验吗?,haskell,Haskell,编译器抱怨: 无法推断由于使用“bar”而产生的(二进制t0) 从上下文:(二进制t,foot) 受以下类型签名的约束: 复制:福尔t。(二进制t,Foo t)=>代理t->() 无法推断因使用“Foo”而产生的(Foo t2) 从上下文:(二进制t,foot) 受以下类型签名的约束: 复制:福尔t。(二进制t,Foo t)=>代理t->() 具体地说,我很惊讶它没有看到我将t传递到bar,并创建了t0类型变量。t2甚至更神秘,因为foo是用类型t显式注释的,默认情况下,类型变量的作用域不是

编译器抱怨:

  • 无法推断由于使用“bar”而产生的(二进制t0) 从上下文:(二进制t,foot) 受以下类型签名的约束: 复制:福尔t。(二进制t,Foo t)=>代理t->()

  • 无法推断因使用“Foo”而产生的(Foo t2) 从上下文:(二进制t,foot) 受以下类型签名的约束: 复制:福尔t。(二进制t,Foo t)=>代理t->()


具体地说,我很惊讶它没有看到我将
t
传递到
bar
,并创建了
t0
类型变量。
t2
甚至更神秘,因为
foo
是用类型
t

显式注释的,默认情况下,类型变量的作用域不是这样的。函数签名中的
t
与函数体中的
t
不相同。您的代码与此等效:

class Foo t where
  foo :: t

bar :: Binary t => t -> ()
bar = undefined

repro :: (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
  bar (foo :: t)
repro :: (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
  bar (foo :: a)
您需要启用ScopedTypeVariables扩展,并为所有t
添加显式的

repro :: (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
  bar (foo :: a)

您可能需要打开
ScopedTypeVariables
扩展,然后使用

{-# LANGUAGE ScopedTypeVariables #-}

repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
repro _proxy =
  bar (foo :: t)
否则,
foo::t
中的
t
repro
签名中的另一个
t
不相关。本质上,
foo::t
等同于
foo::forall a。a


这可以说是Haskell定义中最不喜欢的特性之一,
ScopedTypeVariables
非常流行,因为它允许解决这个问题。(在我看来,默认情况下它应该是打开的。)

为了完整性,这也可以在没有扩展的情况下处理。这里的关键技巧是编写一个类型比需要限制得更多的函数,将
代理
的type参数与
Foo
实例连接起来。因此:

repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
repro _proxy = bar (foo :: t)
当然,现代的方法是使用更多的扩展来完全消除代理:

-- most general possible type is Foo b => a -> b
fooForProxy :: Foo t => proxy t -> t
fooForProxy _proxy = foo

-- I've changed Proxy to proxy here because that's good practice, but everything
-- works fine with your original signature.
repro :: (Binary t, Foo t) => proxy t -> ()
repro = bar . fooForProxy
调用
repro
将再次需要一个类型应用程序,如
repro@Int
或其他任何类型