Core Haskell将类型应用于函数意味着什么? [英] What does Core Haskell applying types to functions mean?

查看:103
本文介绍了Core Haskell将类型应用于函数意味着什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



下面是我运行漂亮打印机的模块代码:

  module Bar2 where 

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

add2 ab = a + b

以下是漂亮的打印机输出:

  --------------- ----------------模块元数据-------------------------------- 
Module {main:: modulePackageId,Bar2:: moduleName}
-------------------------- ------类型绑定---------------------------------
[r0: - > ;标识符'add',rjH: - >标识符'add2']
--------------------------------核心绑定------- --------------------------
NonRec(Idadd2)
(Lam(TyVara)
(Lam(Idb))
(Lam(Id$ dNum)
(Lam(Ida1)
(App(App(App (Id+))
(Type(TyVar(TyVara))))
(Var(Id$ dNum)))
(Var(Ida1 )))
(Var(Idb))))))

NonRec(Idadd)
(Lam(Ida)$ b (应用程序(应用程序(Var(Id+))
(类型(TyConApp(Int)[])))$ b $(Lam(Idb)
b(Var(Id$ fNumInt)))
(Var(Ida)))
(Var(Idb)))))
---- -------------------- ---------安全的Haskell ---------------------------------
安全
-------------------------------------结束--------- -----------------------------

令我困惑的是,在这两种情况下,Core似乎都是在 + 函数中应用类型变量或类型构造函数,以及一些 $ dNum $ fNumInt ,然后接受参数。



对于添加函数,类型也是明确给出的,而 add2 留下编译器推理。这似乎也影响了lambda函数链需要评估的参数个数,其中 add 需要2,而 add2 要求4。



这是什么意思?

解决方案

核心几乎是 SystemF (技术上 SystemFC )。在SystemF中,类型变量也需要是该函数的参数。在你的例子中,Haskell推断:

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

这解释了 TyVar a参数 add2

另外,Haskell必须找到一种方法根据参数 a 和<$ c的类型,将其分配给 Num 函数的'right' $ c> b 是。它通过为每个类型类约束创建一个字典参数。这是 Id $ dNum 参数。在 add 的情况下,Haskell已经知道哪个字典可以找到合适的(+)函数,因为它知道它知道该操作在 Int (所以它不需要传入:它只是 $ fNumInt )。

基本上,发生的是,对于每个类型类Haskell创建一个记录 data $ d< Class> = ... ,这些字段是类型类中的函数。然后,对于每个实例,它使另一个 $ f< Class>< Type> :: $ d< Class> 这里有更详细的解释



这是另一个很好的回答,描述了与核心相关的事情。 p>

I wrote a custom pretty printer for Core Haskell in order to better study Core's structure. 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.

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.

For the add function, the type is also explicitly given, while the add2 is left up to compiler inference. 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.

What does this all mean?

解决方案

Core is pretty much SystemF (technically SystemFC). In SystemF, type variables also need to be arguments to the function. In your example, Haskell infers that

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

That explains the TyVar "a" argument to 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. It does that by having a dictionary argument for each type class constraint. That's the Id $dNum argument. 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).

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. Then, for each instance, it makes another $f<Class><Type> :: $d<Class>. This is explained in more detail here

Here is another excellent answer describing Core related things.

这篇关于Core Haskell将类型应用于函数意味着什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆