【问题标题】:What does Core Haskell applying types to functions mean?Core Haskell 将类型应用于函数是什么意思?
【发布时间】:2016-11-09 23:12:20
【问题描述】:

为了更好地研究 Core 的结构,我为 Core Haskell 编写了一个定制的漂亮打印机。这个漂亮的打印机的要点是它需要一个CoreModule 并在输出中包含数据构造函数,而默认的Outputable 实现似乎没有这样做。

这是我正在运行漂亮打印机的模块的代码:

module Bar2 where

add :: Int -> Int -> Int
add a b = a + b

add2 a b = a + b

这是漂亮的打印机输出:

------------------------------- Module Metadata --------------------------------
Module { "main" :: modulePackageId, "Bar2" :: moduleName }
-------------------------------- Type Bindings ---------------------------------
[r0 :-> Identifier ‘add’, rjH :-> Identifier ‘add2’]
-------------------------------- Core Bindings ---------------------------------
NonRec (Id "add2")
       (Lam (TyVar "a")
            (Lam (Id "$dNum")
                 (Lam (Id "a1")
                      (Lam (Id "b")
                           (App (App (App (App (Var (Id "+"))
                                               (Type (TyVar (TyVar "a"))))
                                          (Var (Id "$dNum")))
                                     (Var (Id "a1")))
                                (Var (Id "b")))))))

NonRec (Id "add")
       (Lam (Id "a")
            (Lam (Id "b")
                 (App (App (App (App (Var (Id "+"))
                                     (Type (TyConApp (Int) [])))
                                (Var (Id "$fNumInt")))
                           (Var (Id "a")))
                      (Var (Id "b")))))
--------------------------------- Safe Haskell ---------------------------------
Safe
------------------------------------- End --------------------------------------

让我感到困惑的是,在这两种情况下,Core 似乎都在将类型变量或类型构造函数应用于 + 函数,以及一些 $dNum$fNumInt 在接受参数之前.

对于add 函数,类型也是明确给出的,而add2 则留给编译器推断。这似乎也影响了 lambda 函数链求值所需的参数数量,add 需要 2 而add2 需要 4。

这一切意味着什么?

【问题讨论】:

    标签: function haskell types ghc lambda-calculus


    【解决方案1】:

    核心几乎是SystemF(技术上是SystemFC)。在 SystemF 中,类型变量也需要作为函数的参数。在您的示例中,Haskell 推断出

    add2 :: Num a => a -> a -> a 
    add2 a b = a + b
    

    这解释了TyVar "a"add2 的参数。

    此外,Haskell 必须找到一种方法,根据 ab 的参数类型来分派到“正确”的 Num 函数集。它通过为每个类型类约束提供一个字典参数来做到这一点。这就是Id $dNum 论点。在add 的情况下,Haskell 已经知道哪个字典可以找到合适的(+) 函数,因为它知道它知道操作在Int 上(所以它不需要传入:它只是@987654335 @)。

    本质上,幕后发生的事情是,对于每个类型类,Haskell 都会记录data $d<Class> = ...,其中的字段是类型类中的函数。然后,对于每个实例,它都会生成另一个 $f<Class><Type> :: $d<Class>This is explained in more detail here

    Here is another excellent answer describing Core related things.

    【讨论】:

      【解决方案2】:

      在 GHC 8.x 中,您也可以在 Haskell 中使用类型参数,类似于 Core。这是一个基于发布的代码的带有更多注释的示例。

      add :: Int -> Int -> Int
      add a b = (+) @ Int a b
      

      (+) @ Int 特化了多态 (+) 运算符,使其适用于类型 Int

      在 Core 中,您还可以看到类型类字典在 $fNumInt 周围传递。

      add2 :: forall n. Num n => n -> n -> n    
      add2 a b = (+) @ n a b
      

      这个基本一样,只是n不知道。

      在 Core 中,add2 采用隐藏的“类型值”参数n(在发布的示例中令人困惑地称为a,即(Lam (TyVar "a") ...),然后将其作为类型参数转发给(+) .由于字典现在未知,Core 中还有另一个隐藏参数:字典必须由add2 的调用者传递,然后将其转发给(+)。这个附加参数称为$dNum(参见(Lam (Id "$dNum") ...)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-04-29
        • 2022-01-02
        • 2011-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多