2016-07-07 106 views
3

我为Core Haskell编写了一个自定义的漂亮打印机,以便更好地研究Core的结构。这台漂亮的打印机的要点是它需要一个CoreModule,并在输出中包含数据构造函数,默认情况下Outputable实现看起来没有。Core Haskell将函数应用于函数是什么意思?

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

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

这是什么意思?

回答

7

核心几乎是SystemF(技术上SystemFC)。在SystemF中,类型变量也需要是该函数的参数。在你的榜样,哈斯克尔推断

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

这解释了TyVar "a"参数add2

此外,Haskell必须找到一种方法,根据参数ab的类型是什么类型,调度到“右”功能集合Num功能。它通过为每个类型类约束创建一个字典参数。这是Id $dNum的说法。在add的情况下,Haskell已经知道哪个字典可以找到合适的(+)函数,因为它知道它知道该操作在Int(所以不需要传入:它只是$fNumInt)。

本质上,发生在引擎盖下的是,对于每个类型类Haskell创建了一个带有typeclass内部函数的字段的记录data $d<Class> = ...。然后,对于每个实例,它会产生另一个$f<Class><Type> :: $d<Class>This is explained in more detail here

Here is another excellent answer describing Core related things.

5

在GHC 8.x中可以用在Haskell类型的参数发挥好,与核心。以下是一些基于发布代码的更多注释示例。

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是未知的。

在核心,add2需要隐藏的“类型值”参数n(混淆性称为a在贴例子,即(Lam (TyVar "a") ...),然后将其转发到(+)作为类型参数。由于字典现在是未知的,所以在Core中有另一个隐藏的参数:字典必须由调用者add2传递,然后将其转发到(+)。这个额外的参数被称为$dNum(见(Lam (Id "$dNum") ...)。

相关问题