Last week I introduced some constructs to make programming games with Haskell easier, mostly the idea of an accessor for dealing nicely with highly stateful functions. The (refined once) abstraction looked like this:
data Accessor s a
= Accessor { getVal :: forall m. MonadState s m => m a
, setVal :: forall m. MonadState s m => a -> m ()
}
The theory was, you had your global state data structure, and you could define accessors into pieces of it. So for example, if you had the following state:
data Foo = Foo { p1score_ :: Int, p2score_ :: Int }
You (or a TH module) would define:
p1score = Accessor (gets p1score_) (x -> get >>= s -> s { p1score_ = x })
p2score = Accessor (gets p2score_) (x -> get >>= s -> s { p2score_ = x })
And then given the Accessor abstraction you could do stuff like define a := operator for readability and whatnot. I was proud that you could write accessors that accessed things other than the top level of the data structure.
But something still wasn’t right. I wanted to be able to do something like a.b.c in OO langauges, where a, b, and c were accessors. So here is my new Accessor abstraction:
The idea is that an Accessor is an object which accesses a value of type a as a function of a value of type s (I call it s for state, the typical value). But it no longer has anything to do with a monad, it’s an abstraction simply for extracting data from other data. The new signature is:
data Accessor s a
= Accessor { getVal :: s -> a
, setVal :: a -> s -> s
}
getVal retrieves an a from an s, and setVal inserts an a into an already existing s.
UPDATE: I require the following laws to hold in order to ensure that this is actually behaving as an accessor:
getVal a (setVal a x s) == x setVal a (getVal a s) s == s
As usual in Haskell, this simplified version is much more powerful than the more complex one above. In particular, we may define a composition operator:
(@.) :: Accessor a b -> Accessor b c -> Accessor a c
f @. g =
Accessor { getVal = getVal g . getVal f
, setVal = c a -> setVal f (setVal g c (getVal f a)) a
}
That is to say, @. takes an accessor from a to b and from b to c and generates a way to access c from a, bidirectionally.
We may also define the previous State monady getVal and setVal like so:
getA :: MonadState s m => Accessor s a -> m a getA acc = fmap (getVal acc) get putA :: MonadState s m => Accessor s a -> a -> m () putA acc x = get >>= put . setVal acc x modA :: MonadState s m => Accessor s a -> (a -> a) -> m () modA acc f = fmap f (getA acc) >>= putA acc
Given the appropriate (automatable) definition of accessors for foo and bar, we can do things like this:
data Foo =
Foo { foo_ :: Foo
, bar_ :: Int
}
modA (foo @. bar) (+1) -- increment foo.bar by 1
liftIO . print =<< foo @. foo @. foo @. bar
I plan to write a TH generator for such accessors in the near future.
Here is a test demonstrating the idea and showing that it can work.

Very interesting concept :) One small question, however.. Have you actually tried this code? For instance, how do you create a Foo structure, or do you tie the knot?
Btw, once you have setA, obviously it would be the perfect candidate for :=
Cheers, and thanks for the interesting read :)
I didn’t try the code at the end; having Foo refer to itself was just a lazy way to demonstrate @. without having to declare two separate datas. But creating a Foo is easy enough:
let mkfoo n = Foo { foo_ = mkfoo (n+1), bar_ = n } in mkfoo 0