Question

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?

Was it helpful?

Solution

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

OTHER TIPS

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).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top