简体   繁体   English

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

[英]What does Core Haskell applying types to functions mean?

I wrote a custom pretty printer for Core Haskell in order to better study Core's structure. 我为Core Haskell编写了一个定制漂亮的打印机,以便更好地研究Core的结构。 The gist of this pretty printer is that it takes a CoreModule and includes data constructors in the output, which the default Outputable implementation does not seem to do. 这个漂亮的打印机的要点是它需要一个CoreModule并在输出中包含数据构造函数,默认的Outputable实现似乎没有。

Here is the code of the module that I am running the pretty printer on: 这是我运行漂亮的打印机的模块的代码:

module Bar2 where

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

add2 a b = a + b

Here is the pretty printer output: 这是漂亮的打印机输出:

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

What is confusing to me is that in both instances, Core appears to be applying a type variable, or a type constructor to the + function, as well as some $dNum or $fNumInt before taking in the arguments. 令我感到困惑的是,在两个实例中,Core似乎在接受参数之前将一个类型变量或类型构造函数应用于+函数,以及一些$dNum$fNumInt

For the add function, the type is also explicitly given, while the add2 is left up to compiler inference. 对于add函数,还明确给出了类型,而add2则留给编译器推断。 This also seems to affect the number of arguments that the chain of lambda functions requires for evaluation, with add needing 2 while add2 requiring 4. 这似乎也会影响lambda函数链需要进行评估的参数数量, add需要2,而add2需要4。

What does this all mean? 这是什么意思呢?

Core is pretty much SystemF (technically SystemFC ). Core几乎是SystemF (技术上是SystemFC )。 In SystemF, type variables also need to be arguments to the function. 在SystemF中,类型变量也需要是函数的参数。 In your example, Haskell infers that 在您的示例中,Haskell推断出这一点

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

That explains the TyVar "a" argument to add2 . 这也解释了TyVar "a"参数add2

Also, Haskell has to find a way to dispatch to the 'right' set of Num functions depending on what the type of the arguments a and b is. 此外,Haskell必须找到一种方法来分配到'正确'的Num函数集,具体取决于参数ab类型。 It does that by having a dictionary argument for each type class constraint. 它通过为每个类型类约束提供字典参数来实现。 That's the Id $dNum argument. 这是Id $dNum参数。 In the case of add , Haskell already knows which dictionary the appropriate (+) function can be found since it knows it knows the operation is on Int (so it doesn't need to be passed in: it's just $fNumInt ). add的情况下,Haskell已经知道哪个字典可以找到适当的(+)函数,因为它知道它知道操作在Int (所以它不需要传入:它只是$fNumInt )。

Essentially what happens under the hood is that for each typeclass Haskell makes a record data $d<Class> = ... with fields that are the functions inside the typeclass. 本质上发生的事情是,对于每个类型类,Haskell创建一个记录data $d<Class> = ...其中字段是类型类中的函数。 Then, for each instance, it makes another $f<Class><Type> :: $d<Class> . 然后,对于每个实例,它会生成另一个$f<Class><Type> :: $d<Class> This is explained in more detail here 这在这里有更详细的解释

Here is another excellent answer describing Core related things. 这是另一个描述Core相关事物的优秀答案。

In GHC 8.x you can play with type arguments in Haskell as well, similarly to Core. 在GHC 8.x中,您也可以在Haskell中使用类型参数,类似于Core。 Here's an example with some more annotations, based on the posted code. 这是一个带有更多注释的示例,基于发布的代码。

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

The (+) @ Int specializes the polymorphic (+) operator so that it works on type Int . (+) @ Int专门用于多态(+)运算符,因此它适用于Int类型。

In Core, you also see the typeclass dictionary being passed around $fNumInt . 在Core中,您还可以看到在$fNumInt周围传递的类型类字典。

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

This is basically the same, except that n is not known. 这基本上是相同的,除了n是未知的。

In Core, add2 takes a hidden "type-valued" argument n (confusingly called a in the posted example, ie (Lam (TyVar "a") ... ), which is then forwarded to (+) as a type argument. Since the dictionary is now unknown, in Core there is another hidden argument: the dictionary has to be passed by the caller of add2 , which then forwards it to (+) . This additional argument is called $dNum (See (Lam (Id "$dNum") ... ). 在Core中, add2采用隐藏的“类型值”参数n (在发布的示例中混淆地称为a ,即(Lam (TyVar "a") ... ),然后将其作为类型参数转发到(+) 。由于字典现在是未知的,在Core中还有另一个隐藏的参数:字典必须由add2的调用者传递,然后将其转发到(+) 。这个额外的参数称为$dNum (参见(Lam (Id "$dNum") ... )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM