gpt4 book ai didi

haskell - 如何将 TypeApplications 与 typeclass 方法一起使用,为什么 GHCi 会推断出我无法使用的类型?

转载 作者:行者123 更新时间:2023-12-03 13:32:28 24 4
gpt4 key购买 nike

概括
我有一个类型类,我想为它写一些“通用术语”。我有两个问题:

  • 使用 :t向 GHCi 询问通用术语的类型有效,但使用该推断类型失败 - 为什么?
  • 如何使用 TypeApplications使用类型类的方法?

  • 我正在使用 GHC 8.8.4 .对于这两个问题,我有以下示例 Main.hs包含一个类型类 F并输入 Empty这是 F 的一个实例.
    {-# LANGUAGE NoStarIsType #-}
    {-# LANGUAGE PolyKinds #-}
    {-# LANGUAGE AllowAmbiguousTypes #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeApplications #-}
    module Main where

    import GHC.Types (Type)

    class F (f :: k -> Type) where
    type Plus f (a :: k) (b :: k) :: k

    zero :: f a
    plus :: f a -> f b -> f (Plus f a b)

    data Empty (a :: Type) = Empty

    instance F Empty where
    type Plus Empty a b = (a, b)
    zero = Empty
    plus _ _ = Empty

    1. 推断类型不起作用?
    我想构造类型类的通用术语 F .例如, plus zero zero .
    当我向 GHCi 询问这个术语的类型时,它给了我我的期望:
    *Main> :t plus zero zero
    plus zero zero :: F f => f (Plus f a b)
    令人惊讶的是,如果我尝试分配这个术语,我会得到一个错误。也就是说,如果我将以下内容添加到 Main.hs :
    -- This doesn't work.
    plusZero :: F f => f (Plus f a b)
    plusZero = plus zero zero
    在 GHCi 中重新加载文件报错:
        • Couldn't match type ‘Plus f a0 b0’ with ‘Plus f a b’
    Expected type: f (Plus f a b)
    Actual type: f (Plus f a0 b0)
    NB: ‘Plus’ is a non-injective type family
    The type variables ‘a0’, ‘b0’ are ambiguous
    • In the expression: plus zero zero
    In an equation for ‘plusZero’: plusZero = plus zero zero

    我的第一个问题是:为什么 GHCi 似乎推断类型,但当我明确注释该术语时拒绝它?
    2.使用 TypeApplications而不是注释
    我可以简单地通过注释 zero 的类型来解决第一个问题。条款:
    -- This works
    plusZero1 :: forall f a b . F f => f (Plus f a b)
    plusZero1 = plus (zero :: f a) (zero :: f b)
    但是,当条款变大时,这有点笨拙。我想做的是使用 TypeApplications .我试过这个:
    -- This doesn't work
    plusZero2 :: forall f a b . F f => f (Plus f a b)
    plusZero2 = plus @f @a @b zero zero
    但GHCI提示:
        • Expecting one more argument to ‘f’
    Expected a type, but ‘f’ has kind ‘k -> *’
    • In the type ‘f’
    In the expression: plus @f @a @b zero zero
    In an equation for ‘plusZero2’: plusZero2 = plus @f @a @b zero zero
    • Relevant bindings include
    plusZero2 :: f (Plus f a b) (bound at Main.hs:36:1)
    奇怪的是,如果我先定义附加函数 plus'zero'如下,一切都按预期工作:
    zero' :: forall f a . F f => f a
    zero' = zero

    plus' :: forall f a b . F f => f a -> f b -> f (Plus f a b)
    plus' = plus

    -- This works fine
    plusZero3 :: forall f a b . F f => f (Plus f a b)
    plusZero3 = plus' @f @a @b zero' zero'
    所以看来我还没有理解 TypeApplications适用于类型类方法。
    如何将类型应用程序与 plus 一起使用和 zero无需定义附加函数 plus'zero' ?

    最佳答案

    1. Inferred Types don't work?

    在您的示例中,GHC 确实可以推断类型,但它不能接受您的签名。这似乎违反直觉,但如果您考虑总体情况,它确实是有道理的。 Plus f a b是一个非内射类型族。对于所有 GHC 在类型检查时所知道的,它可以定义为 Plus f a b = a为所有 f , a , 和 b .
    假设我们已经定义了一个术语(为了清楚起见,我添加了 forall s)
    foo :: forall f a b. F f => f (Plus f a b)
    我们写
    bar :: forall f a b. F f => f (Plus f a b)
    bar = foo
    这不应该输入检查(!),因为它本质上是模棱两可的。作为人类的程序员可能期望编译器推断出这些类型:
    bar :: forall f a b. F f => f (Plus f a b)
    bar = foo @f @a @b
    但是,可能还有其他正确的推断类型!确实,如果 Plus如上所述定义,这也将类型检查:
    bar :: forall f a b. F f => f (Plus f a b)
    bar = foo @f @a @String
    使用它, foo将产生 f (Plus f a String)f (Plus f a b) 相同,所以一切类型检查。因为程序员可能打算使用 @b 以外的其他东西。 ,我们在这里停止报告类型错误的歧义。

    从技术上讲,推理过程中发生的情况是:调用 poltmorphic foo链接到新鲜的未知类型变量:
    bar :: forall f a b. F f => f (Plus f a b)
    bar = foo @xf @xa @xb
    然后,统一发生: foo @xf @xa @xb 的类型是 xf (Plus xf xa xb)这与提供的签名统一以查找未知数:
    xf (Plus xf xa xb) ~ f (Plus f a b)
    由此我们应用统一算法:
    xf ~ f
    Plus xf xa xb ~ Plus f a b
    所以我们找到未知的类型 xf , 代入我们得到:
    xf ~ f
    Plus f xa xb ~ Plus f a b
    然而,我们到此为止。我们无法推断 xa ~ axb ~ b因为类型族不是单射的。

    1. Using TypeApplications instead of annotations

    问题是有一个隐藏的种类 @k论点,因为这发生在类里面。使用 :t +v用所有 forall 显示真实类型年代:
    > :t +v plus
    plus
    :: forall k (f :: k -> *) (a :: k) (b :: k).
    F f =>
    f a -> f b -> f (Plus f a b)
    路过 @k也有效:
    plusZero2 :: forall k (f :: k -> Type) a b . F f => f (Plus f a b)
    plusZero2 = plus @k @f @a @b zero zero
    或者,让编译器推断 @k :
    plusZero2 :: forall f a b . F f => f (Plus f a b)
    plusZero2 = plus @_ @f @a @b zero zero

    关于haskell - 如何将 TypeApplications 与 typeclass 方法一起使用,为什么 GHCi 会推断出我无法使用的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65369389/

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