Writing a simple Core evaluator, having trouble with name lookups

Csaba Hruska csaba.hruska at gmail.com
Sat Dec 1 15:25:36 UTC 2018


That's right there are some missing bindings at the core level, those will
be generated by *corePrepPgm* function.
STG is a low level version of core and it contains all code that is
required for execution. Classes are represented as nodes (dictionary) at
STG level.
E.g. here is my custom STG data type which I export:
https://github.com/grin-tech/ghc-grin/blob/master/ghc-dump-core/GhcDump_StgAst.hs
In my opinion GHC core has lots of internal coding convention.

Cheers


On Sat, Dec 1, 2018 at 4:02 PM Christopher Done <chrisdone at gmail.com> wrote:

> Regarding classes,
>
> Csaba, did you have to deal with dictionaries at the STG level? I'm
> finding that at the Core level, methods don't generate code, so I have
> to generate them myself. But then I have to know more about classes
> than I might ideally not want to.
>
> For example, I've noticed that if a class has only one method, then
> the dictionary for an instance doesn't actually seem to construct a
> record, but makes a special case and just refers to the single method.
> So my evaluator failed on this. If I add one more method to the class,
> then things work properly. Example:
>
> module Demo (demo) where
> class Person a where
>   person :: a -> Int
>   wibble :: a -> Int
> instance Person X where
>   person unusedarg = 5
>   wibble _ = 9
> data X = X
> demo = person X
>
> This produces
>
> chris at precision:~/Work/chrisdone/prana$ sh scripts/compiledemo.sh
> [1 of 1] Compiling Demo             ( Demo.hs, Demo.o )
> Writing main_Demo.prana
> Eval: ((main:Demo.person main:Demo.$fPersonX) main:Demo.X)
>   Eval: (main:Demo.person main:Demo.$fPersonX)
>     Eval: main:Demo.person
>     Done: main:Demo.person[Method]0
>     Eval: main:Demo.$fPersonX   <---- here is the dictionary, and you
> can see what it refers to below:
>       Eval: ((main:Demo.C:Person main:Demo.$cperson)
> main:Demo.$cwibble) <-- this is the two methods
>         Eval: (main:Demo.C:Person main:Demo.$cperson)
>           Eval: main:Demo.C:Person
>           Done: (main:Demo.C:Person[Con] )
>         Done: (main:Demo.C:Person[Con] main:Demo.$cperson)
>       Done: (main:Demo.C:Person[Con] main:Demo.$cpersonmain:Demo.$cwibble)
>     Done: (main:Demo.C:Person[Con] main:Demo.$cpersonmain:Demo.$cwibble)
>     Eval: main:Demo.$cperson
>       Eval: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>       Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>     Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>   Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>   Eval: (ghc-prim:GHC.Types.I# 5)
>     Eval: ghc-prim:GHC.Types.I#
>     Done: (ghc-prim:GHC.Types.I#[Con] )
>   Done: (ghc-prim:GHC.Types.I#[Con] 5)
> Done: (ghc-prim:GHC.Types.I#[Con] 5)
> ConWHNF (Id {idStableName = "ghc-prim:GHC.Types.I#", idUnique = Unique
> 3891110078048108563, idCategory = DataCat}) [LitE (Int 5)]
>
> That's great. But if I delete the wibble method:
>
> Eval: ((main:Demo.person main:Demo.$fPersonX) main:Demo.X)
>   Eval: (main:Demo.person main:Demo.$fPersonX)
>     Eval: main:Demo.person
>     Done: main:Demo.person[Method]0
>     Eval: main:Demo.$fPersonX <- the dictionary
>       Eval: main:Demo.$cperson  <-- evaluates to simply the person
> method, instead of a data constructor
>         Eval: main:Demo.$cperson
>           Eval: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>           Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>         Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>       Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>     Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>
> Which results in a runtime type error:
>
> prana: TypeError (NotAnInstanceDictionary (Id {idStableName =
> "main:Demo.person", idUnique = Unique 8214565720323785170, idCategory
> = ValCat}) (LamWHNF (Id {idStableName = "main:Demo.unusedarg",
> idUnique = Unique 6989586621679011036, idCategory = ValCat}) (AppE
> (VarE (Id {idStableName = "ghc-prim:GHC.Types.I#", idUnique = Unique
> 3891110078048108563, idCategory = DataCat})) (LitE (Int 5)))))
>
> I could ignore the fact that I got a function instead of a dictionary,
> and then evaluation proceeds OK:
>
> Eval: ((main:Demo.person main:Demo.$fPersonX) main:Demo.X)
>   Eval: (main:Demo.person main:Demo.$fPersonX)
>     Eval: main:Demo.person
>     Done: main:Demo.person[Method]0
>     Eval: main:Demo.$fPersonX
>       Eval: main:Demo.$cperson
>         Eval: main:Demo.$cperson
>           Eval: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>           Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>         Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>       Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>     Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>   Done: (\main:Demo.unusedarg -> (ghc-prim:GHC.Types.I# 5))
>   Eval: (ghc-prim:GHC.Types.I# 5)
>     Eval: ghc-prim:GHC.Types.I#
>     Done: (ghc-prim:GHC.Types.I#[Con] )
>   Done: (ghc-prim:GHC.Types.I#[Con] 5)
> Done: (ghc-prim:GHC.Types.I#[Con] 5)
> ConWHNF (Id {idStableName = "ghc-prim:GHC.Types.I#", idUnique = Unique
> 3891110078048108563, idCategory = DataCat}) [LitE (Int 5)]
>
> But this feels a bit less structured. I want, for example, to be able
> to update definitions at runtime, so runtime type-errors will be a
> thing sometimes. I feel like this kind of thing would make some
> confusing runtime type errors. And what other class-specific oddities
> should I have to handle?
>
> I haven't gotten to dictionaries with superclasses yet, that'll
> require more handling so I'll probably need more knowledge of classes
> anyway.
>
> So I'm wondering whether at the STG phase all classes have been
> abstracted away and we really do only deal with lambdas, lets and
> cases? I didn't see any class-specific code in your project.
>
> I only chose Core because I wanted to stay as close to the original
> Haskell source as possible, and have a very simple evaluation model,
> but perhaps STG is the easier choice.
>
> Cheers
> On Sat, 1 Dec 2018 at 12:42, Csaba Hruska <csaba.hruska at gmail.com> wrote:
> >
> > The package name + module name is always unique in every Haskell
> program, module names can not be duplicated in a package and package names
> are unique also.
> > There are 2 kinds of identifiers, local and exported.
> > You can construct a whole program unique name for:
> >
> > exported identifier with combining the package name + module name +
> occurence name (without the unique value)
> > local identifier with combining the package name + module name +
> occurence name + unique (what is unique per invocation)
> >
> > It is safe because only the exported names can appear in an external
> expression and those do not contain the GHC's unique value.
> > Just think about how the object code linker deals with GHC symbols.
> >
> > Cheers,
> > Csaba
> >
> > On Sat, Dec 1, 2018 at 1:23 PM Christopher Done <chrisdone at gmail.com>
> wrote:
> >>
> >> I think what Csaba means is that we can have e.g.
> >>
> >> * GHC invocation 1
> >>   * ghc-prim:
> >>     * MyModule.foo has Unique 123
> >>     * OtherModule.bar has Unique 124
> >> * GHC invocation 2
> >>   * base:
> >>     * MyMod.mu has Unique 123
> >>     * OtherMod.zot has Unique 124
> >>
> >> For a unique reference then, we just need:
> >>
> >> * ghc-prim:MyMobile.foo+123
> >> * ghc-prim:OtherModule.bar+124
> >> * base:MyMod.mu+123
> >> * base:OtherMod.zot+124
> >>
> >> For any local lookup this is reliable. If the lookup fails, a lookup
> >> without the Unique works for cross-module lookups.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20181201/7cdb9b58/attachment.html>


More information about the ghc-devs mailing list