On Mon, Jan 02, 2006 at 07:36:03PM +0100, Ch. A. Herrmann wrote:
Hi Tomasz,
Hi!
It seems that you are a bit confused with monads. The only monad you have here is the Q type constructor.
maybe you misunderstood me. Take the example of the ST monad. If you are in an ST monadic computation with state, then apply a non-monadic function in which you run a ST monad again, you are faced with two different states.
Well, yes, but that's because there is runST :: (forall s. ST s a) -> a With Q there is no such function, or at least you are not supposed to use it in normal code.
As long as you finished the second monadic computation before you continue the first, this will not be a problem. But if you are forced to insert a monadic value (not a return value) from the second activation into the first activation, you have two different states, and you have to consider how to proceed. If you copy parts of the second activation into the first state, you'll have problem when you leave the second and continue the first activation. Thus, the only serious solution I can figure out at the moment is to ignore the state of the second activation when inserting a value from the second one in the first one.
The type of runST uses local universal quantification exactly to prevent you from mixing things that belong to different State Threads. All ST references, mutable arrays have a thread tag - that's what the 's' type parameter is for.
Coming back to the Q monad this would mean: ignore the name generation history from the point where the expression to be spliced was *created* and take the name generation history from the point where the splice is to be *inserted*, which is an older one.
When you define var = [| $(liftM VarE (newName "a")) |] there is no name generation history involved (at the place of definition). This definition should be equivalent to var = liftM VarE (newName "a") It is only when you actually run this code, for example in a top-level splice, when the name generation history comes into play. Think of Q as type Q a = NameGenerationState -> (a, NameGenerationState) Such a function can exist without NameGenerationState. NameGenerationState is only neccesary when you want to compute the result of such function, not when you are only using it to build more and more complicated functions.
Anyway, the names inside the generated splice take the newer information from the lexical scope of the creation.
I am not sure, but I think that lexical scopes in TH are implemented indepentendly from name generation and the Q monad. Seems like I have to read the TH papers again.
However, I'm worried about that by turning an expression into a monadic form just by using *return* tags this splice with a blank name history and I hope that the quasi quote implementation will never proceed with this history but with the one from the lexical sope of the insertion point.
Monad laws state that "return" should be a pretty harmless function: (return x) >>= f == f x m >>= return == m
Unfortunately, I've had too many bad experiences which make me feel not that optimistic, especially when working with state.
Could you describe your experiences in more detail? I can see that playing with experimental stuff like Template Haskell or with inherently unsafe features like the FFI can be less safe, but when you exclude those, it's quite difficult to break things by accident, or even on purpose! Best regards Tomasz -- I am searching for a programmer who is good at least in some of [Haskell, ML, C++, Linux, FreeBSD, math] for work in Warsaw, Poland