代码之家  ›  专栏  ›  技术社区  ›  luntain

为什么这种类型不检查?

  •  2
  • luntain  · 技术社区  · 7 年前
    class Foo t where
      foo :: t
    
    bar :: Binary t => t -> ()
    bar = undefined
    
    repro :: (Binary t, Foo t) => Proxy t -> ()
    repro _proxy =
      bar (foo :: t)
    

    编译器抱怨:

    • 无法推断(二进制t0)是由于使用了“bar” 来自上下文:(binary t,foo t) 由类型签名绑定: repr::forall t.(二进制t,Foo t)=>代理t->()

    • 无法推断(Foo t2)是由于使用了-Foo 来自上下文:(binary t,foo t) 由类型签名绑定: repr::forall t.(二进制t,Foo t)=>代理t->()

    特别是,我很惊讶它没有看到我经过 t bar ,并创建 t0 类型变量。 t2 更神秘,因为 foo 用类型显式注释 T型 是的。

    3 回复  |  直到 7 年前
        1
  •  4
  •   dcastro    7 年前

    默认情况下,类型变量的作用域不是这样的这个 t 在函数签名和 t型 在功能体上是不一样的。您的代码相当于:

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

    您需要启用scopedTypeVariables扩展,并添加显式 forall t .

    {-# LANGUAGE ScopedTypeVariables #-}
    
    repro :: forall t. (Binary t, Foo t) => Proxy t -> ()
    repro _proxy =
      bar (foo :: t)
    
        2
  •  2
  •   chi    7 年前

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

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

    否则 t 在里面 foo :: t 与对方无关 t型 repro 签名。基本上 福:t 相当于 foo :: forall a. a .

    这可以说是haskell定义中最不喜欢的特性之一,并且 作用域类型变量 是很受欢迎的,因为它可以解决这个问题。(在我看来,这应该是默认的。)

        3
  •  2
  •   Daniel Wagner    7 年前

    为了完整性,这也可以在没有扩展的情况下处理这里的关键技巧是编写一个类型为 更多 比它需要的限制,连接 Proxy 的类型参数 Foo 实例所以:

    -- 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
    

    当然,现代的方法是 更多 完全消除代理的扩展:

    {-# LANGUAGE TypeApplications #-}
    {-# LANGUAGE AllowAmbiguousTypes #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    
    repro :: forall t. (Binary t, Foo t) => ()
    repro = bar @t foo
    

    调用 repro 将再次需要类型应用程序,如 repro @Int 或者别的什么。

    推荐文章