I have an Ast type constructor, parameterized by the identifier type. Using the DeriveFunctor, DeriveFoldable and DeriveTraversable extensions it is possible to automatically create the appropriate instances.
Now I find it useful to introduce more type parameters but unfortunately the above method doesn't scale. Ideally I would like to be able to wrap my Ast type in selection types which would allow me to fmap to the appropriate type parameters. Is there some way to achieve a similar effect without having to define the instances myself?
edit:
Here is a small example of what the original Ast looked like:
Ast id = Ast (FuncDef id)
deriving (Show, Functor, Foldable, Traversable)
FuncDef id = FuncDef id [Fparam id] (Block id)
deriving (Show, Functor, Foldable, Traversable)
Block id = Block [Stmt id]
deriving (Show, Functor, Foldable, Traversable)
..
After fiddling around all day I came to the following conclusions:
The Ast presented in the question turned out not to be very flexible. In later stages I want to annotate different nodes in a way that can't be expressed by just parameterizing the original Ast.
So I changed the Ast in order to act as a base for writing new types:
Now I can simply define a chain of newtypes for each compiler stage. The Ast at the stage of the renamer may be parameterized around the identifier type:
During the typechecking stage the Ast may be parameterized by the different internal types used in the nodes.
This parameterization allows for constructing Asts with
Maybewrapped parameters in the middle of each stage.If everything is ok we can use
fmapto remove theMaybes and prepare the tree for the next stage. There are other waysFunctor, FoldableandTraversableare useful so these are a must to have.At this point I figured that what I want is most likely not possible without metaprogramming so I searched for a template haskell solution. Sure enough there is the genifunctors library which implements generic
fmap, foldMapandtraversefunctions. Using these it's a simple matter of writing a few newtype wrappers to make different instances of the required typeclasses around the appropriate parameters: