I've got the following piece of code that implements a monad. I'm trying to use it to simplify the setting of fields with more complex logic later on.
data Rec = Rec {
alpha :: Int,
beta :: Double,
} deriving (Show)
defaultRec = Rec 0 0 0
data Record r = Record { runRecord :: Rec -> (Rec, r) }
instance Monad Record where
return r = Record $ \s -> (s, r)
a >>= b = Record $ \s -> let (q, r) = runRecord a s in runRecord (b r) q
createRecord f = fst $ runRecord f defaultRec
changeAlpha x = Record $ \s -> (s { alpha = x }, ())
I would use the code like this:
myRecord = createRecord (changeAlpha 9)
This code works, but I'd like to use Template Haskell to simplify the changeAlpha function. It would be great to have something like this:
changeBeta x = $(makeChange beta) x
Now, I've gone as far as this:
changeBeta x = Record $ $([| \z -> \s -> (s { beta = z }, ()) |]) x
But as soon as I change it to this:
changeBeta f x = Record $ $([| \z -> \s -> (s { f = z }, ()) |]) x
I get this:
TestTH.hs:21:49: `f' is not a (visible) constructor field name
No variations work. Is this possible?
The problem is that you can only splice types, expressions or lists of declarations. A record field label is neither of those, so you'll have to use TH combinators to make an expression of type Q Exp
and then splice that, though you can still use Oxford brackets for the remaining parts:
makeChange :: Name -> Q Exp
makeChange x = [|
\z -> Record $ \s -> ( $(recUpdE [| s |] [fieldExp x [| z |]]), () ) |]
To use it, you'll have to quote the name of the field you want to change:
changeBeta :: Double -> Record ()
changeBeta x = $(makeChange 'beta) x
Aren't you just reinventing State(T) monad and lenses?
http://hackage.haskell.org/packages/archive/data-lens/2.0.2/doc/html/Data-Lens-Strict.html
There's data-lens-template code which generates lenses for each "record" starting with _ in a type.
I think f
is just a name; you need to "unquote" it, since it's from the environment, not just stuff inside the quotation [| |]
brackets. I found an example of this here [ link, see section "10.1.1 Select from a tuple" ]. The unquote uses $
, so I imagine your final code would look something like,
changeField f = [| \z s -> s { $(varE f) = z } |]
Unfortunately, my version of ghc (7.0.3) seems to complain about the $
. (It gives a parse error.) Hope this question gets some more attention, it seems good to me (though perhaps remove the unrelated monad part).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With