Core Haskell将类型应用于函数意味着什么?

时间:2016-07-07 14:56:58

标签: function haskell types ghc lambda-calculus

我为Core Haskell编写了一个自定义漂亮的打印机,以便更好地研究Core的结构。这个漂亮的打印机的要点是它需要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或{{1}在接受参数之前。

对于$fNumInt函数,也明确给出了类型,而add由编译器推断。这似乎也会影响lambda函数链需要进行评估的参数数量,add2需要2而add需要4。

这一切意味着什么?

2 个答案:

答案 0 :(得分:7)

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

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

这解释了TyVar "a"的{​​{1}}参数。

此外,Haskell必须找到一种方法来根据add2Num的参数类型调度到“正确”的a函数集。它通过为每个类型类约束提供字典参数来实现。这是b参数。在Id $dNum的情况下,Haskell已经知道哪个字典可以找到适当的add函数,因为它知道它知道操作在(+)上(所以它不需要传递in:它只是Int)。

基本上发生的事情是,对于每个类型类,Haskell使用字段作为类型类中的函数创建记录$fNumInt。然后,对于每个实例,它会生成另一个data $d<Class> = ...This is explained in more detail here

Here is another excellent answer describing Core related things.

答案 1 :(得分:5)

在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采用隐藏的&#34;类型值&#34;参数n(在发布的示例中混淆地称为a,即(Lam (TyVar "a") ...),然后作为类型参数转发给(+)。由于字典现在是未知的,因此在Core中还有另一个隐藏的参数:字典必须由add2的调用者传递,然后将其转发到(+)。这个附加参数称为$dNum(参见(Lam (Id "$dNum") ...)。