2

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.

kylo_el
  • 23
  • 4
  • 2
    I know it's a frustrating non-answer, but you really don't want to mimic OOP in Haskell if your purpose is to accomplish something productive. There are aspects of OOP that can be productively used in Haskell but what you're doing isn't one of them. – bitemyapp Jun 01 '14 at 02:48
  • 3
    You should include code in your question. It is still unclear what you hope to achieve. `Q` is a monad but you don't have to use its monad properties - you can just work with the `QDec`, `QExp`, `QType` types instead. – user2407038 Jun 01 '14 at 04:58
  • 3
    You need to be more specific. What exactly is the problem that you experience? `StateT` composes with `Q` as fine as with any other monad. – Nikita Volkov Jun 01 '14 at 08:49

2 Answers2

4

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:

Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169
  • 1
    Escaping Q monad may by useful for me, but I encountered another problem - if I try to pass `[d| some_declarations |]` to a function, code inside quasiquotes gets checked (although it is not accessible) - for example if I create instance of a class for some type, the type must exist in the outer code or in quotes. Is there a way to avoid that? – kylo_el Jun 01 '14 at 11:52
  • @kylo_el All the definitions referred to must already be present in the context. In this case I'm afraid you can't escape the `Q`. You should check out the "lens" library. They solve the same task in [the `makeClassy` macro](http://hackage.haskell.org/package/lens-4.1.2.1/docs/Control-Lens-TH.html#v:makeClassy). – Nikita Volkov Jun 01 '14 at 12:10
  • Last time I used `StateT` on top of `Q` the result wasn't any more confusing than the `State` monad by itself, but then the only function from `Q` I used in that context was `newName` – Jeremy List Oct 27 '15 at 12:01
  • Also: please don't recommend `unsafePerformIO` for this use case: there are too many functions in the Q monad that don't work properly when this approach is used. – Jeremy List Oct 27 '15 at 12:10
  • @JeremyList If you'll read my question carefully you'll notice that I only recommend using that function for a certain problem area, for which it is safe. – Nikita Volkov Oct 27 '15 at 13:37
0

At least within one file, you can pass state from one DecsQ splice to the one lower down in the file by storing it in:

{-# NOINLINE env #-}
env :: IORef Env
env = unsafePerformIO (newIORef (Env mempty))

and then do things like runIO (readIORef env) :: Q Env.

aavogt
  • 1,308
  • 6
  • 14
  • This is currently the best way. The ideal solution is to use `putQ` and `getQ` but there was a bug in `getQ` that was only fixed a few weeks ago. – Jeremy List Oct 27 '15 at 12:53