Using lenses

Nicolas Trangez nicolas
Thu Oct 3 08:47:11 UTC 2013


Simon,

On Thu, 2013-10-03 at 08:07 +0000, Simon Peyton-Jones wrote:
> If you are using the lens library yourself, could you spare a few
minutes to tell me how you are using it?

I'm not a heavy 'lens'-user (yet), and this might not be the most pretty
use-case from a theoretic point of view, but anyway:

I use 'lens' in Kontiki [1], my implementation of Raft [2], a fairly
recent consensus protocol for distributed systems (think Paxos &
multi-Paxos, Zookeeper,...). The Raft protocol description is fairly
imperative ("When in state S, if you receive a message M, broadcast
message M'(M) to all other nodes, append entry E(M) to your log, and
update the state to S'(S)"), so in order to keep the code close to this
description, I created a TransitionT monad transformer (where the
underlying monad should, in some cases, provide access to the persisted
log of the system, most likely requiring IO access, but this is
abstracted over), which is basically RWST.

The R environment gives access to system configuration (e.g. the set of
nodes (or at least their names/IDs) part of the cluster), W is used to
output/list Commands to be executed as part of a state transition, S
gives access to the current state, and a transition should yield a new
state (or the old one if e.g. a message is received which should be
ignored in the current state). See [3] for an overview of the types
involved.

Finally, after this long introduction, the 'lens' part: thanks to the
'lens' operators, and my types involved in the TransitionT monad
(specifically the R environment and S state) being lens'ified, accessing
R & S becomes fairly unobtrusive and more imperative-looking (which
makes sense when using RWS). As an example, in [4], you see some parts
of the current (Candidate) state being accessed:

    currentTerm <- use cCurrentTerm
    votes <- use cVotes

Not using 'lens', this would be something (OTOH) like

    currentTerm <- fmap _cCurrentTerm get

which is less familiar from an imperative POV.

Then also updating the current state 'in-place' (not for real,
obviously):

    cVotes %= Set.insert sender

And accessing the configuration (R) environment, e.g.:

    nodeId <- view configNodeId

See other handler functions in that module, or in the Follower and
Leader modules in the same directory, for more examples.

Regards,

Nicolas

[1] https://github.com/NicolasT/kontiki
[2]
https://ramcloud.stanford.edu/wiki/download/attachments/11370504/raft.pdf
[3]
https://github.com/NicolasT/kontiki/blob/master/src/Network/Kontiki/Types.hs
[4]
https://github.com/NicolasT/kontiki/blob/master/src/Network/Kontiki/Raft/Candidate.hs#L32





More information about the Libraries mailing list