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?