Boilerplate-free annotation of ASTs in Haskell?

后端 未结 4 975
攒了一身酷
攒了一身酷 2020-12-24 06:53

I\'ve been fiddling around with the Elm compiler, which is written in Haskell.

I\'d like to start implementing some optimizations for it, and part of this involves t

4条回答
  •  死守一世寂寞
    2020-12-24 07:21

    If you leave the recursion in your data type open you end up suffering an extra constructor everywhere, but can layer in annotations freely without changing most of your skeletal tree.

    data Hutton x    -- non-recursive functor type
      = Int Int | Plus x x
      deriving Functor
    
    newtype Tie f = Tie (f (Tie f))
    
    data Annotate f a = Annotate { annotation :: a, layer :: (f (Annotate f a)) }
    
    type Unannotated = Tie      Hutton
    type Annotated a = Annotate Hutton a
    

    This style is much easier when you can write most of your computations as Hutton-algebras since they will compose better.

    interp :: Hutton Int -> Int
    interp (Int i)    = i
    interp (Plus a b) = a + b
    
    runUnannotated :: Functor f => (f x -> x) -> Tie f -> x
    runUnannotated phi (Tie f) = phi (fmap (runUnannotated phi) f)    
    
    runAnnotated :: Functor f => (f x -> x) -> Annotate f a -> x
    runAnnotated phi (Annotate _ f) = phi (fmap (runAnnotated phi) f)
    

    What's also nice is that if you don't mind letting some amount of binding live in the Haskell level (such as in a middling-deep eDSL) then the Free Hutton monad is great for building ASTs and the Cofree Hutton comonad is essentially what Annotated is.

    Here's a way to build annotations from the bottom up.

    annotate :: Functor f => (f b -> b) -> Tie f -> Annotate f b
    annotate phi = runUnannotated $ \x -> Annotate (phi (fmap annotation x)) x
    
    memoize :: Unannotated -> Annotated Int
    memoize = annotate interp
    

    such that with the appropriate Show and Num instances

    λ> memoize (2 + (2 + 2))
    Annotate 6 (Plus (Annotate 2 (Int 2)) (Annotate 4 (Plus (Annotate 2 (Int 2)) (Annotate 2 (Int 2)))))
    

    And here's how you can strip them

    strip :: Annotated a -> Unannotated
    strip = runAnnotated Tie
    

    See here for a description of how you might achieve this kind of AST work with mutually recursive ADTs, insured by Gallais' comment below.

提交回复
热议问题