Search code examples
haskellmonad-transformerstemplate-haskellstate-monad

StateT with Q monad from template haskell


I would like to create a function that takes some declarations of type Dec (which I get from [d| ... |]) and modify them. Modifications will depend on previous declarations so I would like to be able to store them in map hidden in State monad - mainly I create records and class instances and add to them fields from previous records. (This way I want to mimic OOP but this is probably not relevant for my question). I would like to splice results of my computations to code after I processed (and changed) all declarations.

I tried composing StatT with Q every possible way but I can't get it right.

Edit

My idea was to create functions collecting classes declarations (I am aware that Haskell is not object oriented language, I read some papers about how you can encode OOP in Haskell and I try to implement it with Template Haskell as small assignment).

So I would like to be able to write something like this:

declare [d| class A a where 
                attr1 :: a -> Int
                attr2 :: a -> Int |]
declareInherit [d| class B b where
                      attr3 :: b -> Int |] ''A

This is encoding of (for example c++ code)

struct A{
    int attr1;
    int attr2;
}
struct B : A {
    int attr3;
}

And I would like to generate two records with template haskell:

data A_data = A_data { attr1' :: Int, attr2' :: Int}
data B_data = B_data { attr1'' :: Int, attr2'' :: Int, attr3'' :: Int}

and instances of classes:

instance A A_data where
    attr1 = attr1'
    attr2 = attr2'

instance A B_data where
    attr1 = attr1''
    attr2 = attr2''

instance B B_data where
    attr3 = attr3''

This is how my encoding of OO works, but I would like to be able to generate it automatically, not write it by hand.

I had a problem with interacting with DecsQ in declare function, probably I would like it to exist in something like this:

data Env = Env {classes :: Map.Map Name Dec }
type QS a = (StateT Env Q) a

I also has problem how to run computation in QS.


Solution

  • A problem with Template Haskell is that its API is not as polished as in most of other Haskell libraries. The Q monad is overloaded with problems: it reifies, it renders, it manages the state of local names. But it is never a good idea to interleave problem domains, since it's been proven that human beings can only think about one thing at a time (in other words, we're "single core"). This means that mixing problems together is not scalable. That is why it is hard to reason about Q already, and you want to add yet another problem on top of it: your state. Bad idea.

    As well as any other problem you should approach this with separation of concerns. I.e., you should extract smaller problem domains from your main one and work with each of them in isolation. Concerning Template Haskell there are several evident domains: reification of existing ASTs, their analysis and rendering of new ASTs. For communication between those domains you may also need some "lingua franca" data model. Kinda reminds of something, doesn't it? Yep, it's MVC.

    There are certain properties of the extracted domains, which you can then exploit to your benefits: you only need to remain in the Q monad for reification, the rendering and analysis can be done in the pure environment, granting you with all of its benefits. You can safely purify quasi-quotes using unsafePerformIO . runQ.

    For real-life examples I can refer you to some of my projects, in which I apply this approach: