Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I avoid referring to all state variables when updating only a few?

An idiom I use for composing a couple of procedures (with memory) is as follows:

p1 :: State (Int, String) ()
p1 = do
    (a, b) <- get
    ... do something ...
    put (a', b)

p2 :: State (Int, String) ()
p2 = do
    (a, b) <- get
    ... do something else ...
    put (a, b')

main = do
    ... initializing a0 b0 ...
    print . flip evalState (a0, b0)
          . sequence $ replicate 10 p1 ++ repeat p2

However, as the number of state variable grows, this quickly gets way more verbose than necessary:

p1 :: State (Int, String, Bool, Int, String, Bool) ()
p1 = do
    (a, b, c, d, e, f) <- get
    ... do something ...
    put (a, b, c', d, e, f')

p2 :: State (Int, String, Bool, Int, String, Bool) ()
p2 = do
    (a, b, c, d, e, f) <- get
    ... do something ...
    put (a', b', c, d, e, f)

main = do
    print . flip evalState (a0, b0, c0, d0, e0, f0)
          . sequence $ replicate 10 p1 ++ repeat p2

As I was wondering, is there a way of updating only a few state variables without having to refer to all the unused ones? I was thinking something like IORef but for State (in fact there is a package stateref), but I'm not sure if there are already some common idioms that other people have been using.

like image 433
xzhu Avatar asked Nov 10 '16 08:11

xzhu


1 Answers

This seems like a job for lenses. Especially the Control.Lens.Tuple module together with .= and use:

p1 = do
   a <- use _1
   -- do something --
   _1 .= a'

However, it's usually better if you give the things in your state proper names, e.g.

{-# LANGUAGE TemplateHaskell #-

data Record = MkRecord { _age :: Int
                       , _name :: String
                       , _programmer :: Bool
                       } deriving (Show, Eq)
makeLenses ''Record

That way, you have better names for your field:

p1 = do
   a <- use age
   -- do something --
   age .= a'

Note that this still helps you if you don't want to use lenses, since you can use record syntax to update your data:

 p1 = do
      r <- get
      let a = _age r
      --- do something
      put $ r{_age = a'}
like image 129
Zeta Avatar answered Oct 02 '22 18:10

Zeta