State Monad: how to use other Stateful computation on sub-state inside

Hello all, I found myself writing this piece of code: -- | Create and add a new 'Item' to the system sysAddItem :: ItmLabel -> ItmVolume -> ItmCapacity -> Position -> Instant -> State System ItmId sysAddItem lbl vol cap pos t = do sys <- get id <- sysNextId :: State System Id itms' <- return $ execState (itmAdd' (Itm id lbl vol cap pos t)) (sysItems sys) --** modify (\sys -> sys{sysItems=itms'}) return id In the lonely line ** in the middle I use itmAdd' :: Item -> State ItemDb (), where the ItemDb is part of the 'System' and can be extracted via sysItems. I believe this code is correct, but I don't like it. The expression to the right of <- must have the type State System ItemDb But ItemAdd' has the type Item -> State ItemDb () So I need to transform (Item -> State ItemDb ()) to (State System ItemDb). There is no question that I have to pass the Item, but the transformation of the States is quite noisy. Is there a better way to make this transformation, given a function System->ItemDb (i.e. sysItems)?

Hi Martin, I will present a few possibilities so that you can choose one that's available to you. Let's first think about what the code is doing. It is adding an item to the ItemDB. However you are operating on the granulity of a System so you want a function of type:itmAdd :: Item -> System -> System. If we had it then we could write: sysAddItem lbl vol cap pos t = do sys <- get id <- sysNextId :: State SystemId modify $ itmAdd (Itm id lbl vol cap pos t) return id That looks nicer. I'm guessing you don't have itmAdd at hand. One way would be to write it ourselves using itmAdd': itmAdd :: Item -> System -> System itmAdd item s = s{sysItems=execState (itmAdd' item) (sysItems s)} So this solution only hides the ugliness. The reason why it appears is because itmAdd' has a too specific type - an unnecessary State. What it essentially does is it adds an Item to ItemDB, so: itmAdd'' :: Item -> ItemDB -> ItemDB, then the function would look better. I myself would first write itmAdd'' (instead of itmAdd') and then if I really needed itmAdd' in multiple places I would write itmAdd item s = s{sysItems=itmAdd'' item $ sysItems s} itmAdd' item = modify $ itmAdd'' item The code here still may look nicer, but only slightly. My message here is that it is beneficial to think first about what are the most general types that you need and then perhaps splitting them into a combination of other general types. Here the root cause of the ugliness is that you too an unnecessarily specific type of itemAdd'. Best regards, Grzegorz On 16.10.2015 22:28, martin wrote:
Hello all,
I found myself writing this piece of code:
-- | Create and add a new 'Item' to the system sysAddItem :: ItmLabel -> ItmVolume -> ItmCapacity -> Position -> Instant -> State System ItmId
sysAddItem lbl vol cap pos t = do sys <- get id <- sysNextId :: State System Id
itms' <- return $ execState (itmAdd' (Itm id lbl vol cap pos t)) (sysItems sys) --**
modify (\sys -> sys{sysItems=itms'}) return id
In the lonely line ** in the middle I use
itmAdd' :: Item -> State ItemDb (),
where the ItemDb is part of the 'System' and can be extracted via sysItems. I believe this code is correct, but I don't like it.
The expression to the right of <- must have the type
State System ItemDb
But ItemAdd' has the type
Item -> State ItemDb ()
So I need to transform
(Item -> State ItemDb ()) to (State System ItemDb).
There is no question that I have to pass the Item, but the transformation of the States is quite noisy. Is there a better way to make this transformation, given a function System->ItemDb (i.e. sysItems)? _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

This is quite insightful. Now the problem presents itself as follows: In fact I orginally started with plain itmAdd (the one you called itmAdd'') which has no business with State or System. It is just Item->ItemDb -> ItemDb. However, I wanted to use do-notation and made it monadic leading to item' :: Item->State ItemDb () But then it becomes difficult to use it inside State System. Currently I am using subState :: (s1 -> s2) -> State s2 a -> State s1 s2 subState accessor f = do s <- get return $ execState f $ accessor s to make the necessary transformation. I have the feeling that this way of stacking things is a bit inside-out. And as you say, it only hides the ugliness. I will try to stack things bottom up. Am 10/17/2015 um 08:09 AM schrieb Grzegorz Milka:
Hi Martin,
I will present a few possibilities so that you can choose one that's available to you.
Let's first think about what the code is doing. It is adding an item to the ItemDB. However you are operating on the granulity of a System so you want a function of type:itmAdd :: Item -> System -> System. If we had it then we could write:
sysAddItem lbl vol cap pos t = do sys <- get id <- sysNextId :: State SystemId modify $ itmAdd (Itm id lbl vol cap pos t) return id
That looks nicer. I'm guessing you don't have itmAdd at hand. One way would be to write it ourselves using itmAdd':
itmAdd :: Item -> System -> System itmAdd item s = s{sysItems=execState (itmAdd' item) (sysItems s)}
So this solution only hides the ugliness. The reason why it appears is because itmAdd' has a too specific type - an unnecessary State. What it essentially does is it adds an Item to ItemDB, so: itmAdd'' :: Item -> ItemDB -> ItemDB, then the function would look better. I myself would first write itmAdd'' (instead of itmAdd') and then if I really needed itmAdd' in multiple places I would write
itmAdd item s = s{sysItems=itmAdd'' item $ sysItems s} itmAdd' item = modify $ itmAdd'' item
The code here still may look nicer, but only slightly. My message here is that it is beneficial to think first about what are the most general types that you need and then perhaps splitting them into a combination of other general types. Here the root cause of the ugliness is that you too an unnecessarily specific type of itemAdd'.
Best regards, Grzegorz
On 16.10.2015 22:28, martin wrote:
Hello all,
I found myself writing this piece of code:
-- | Create and add a new 'Item' to the system sysAddItem :: ItmLabel -> ItmVolume -> ItmCapacity -> Position -> Instant -> State System ItmId
sysAddItem lbl vol cap pos t = do sys <- get id <- sysNextId :: State System Id
itms' <- return $ execState (itmAdd' (Itm id lbl vol cap pos t)) (sysItems sys) --**
modify (\sys -> sys{sysItems=itms'}) return id
In the lonely line ** in the middle I use
itmAdd' :: Item -> State ItemDb (),
where the ItemDb is part of the 'System' and can be extracted via sysItems. I believe this code is correct, but I don't like it.
The expression to the right of <- must have the type
State System ItemDb
But ItemAdd' has the type
Item -> State ItemDb ()
So I need to transform
(Item -> State ItemDb ()) to (State System ItemDb).
There is no question that I have to pass the Item, but the transformation of the States is quite noisy. Is there a better way to make this transformation, given a function System->ItemDb (i.e. sysItems)? _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

Am 10/17/2015 um 10:01 AM schrieb martin:
This is quite insightful. Now the problem presents itself as follows:
In fact I orginally started with plain itmAdd (the one you called itmAdd'') which has no business with State or System. It is just Item->ItemDb -> ItemDb.
However, I wanted to use do-notation and made it monadic leading to item' :: Item->State ItemDb ()
But then it becomes difficult to use it inside State System. Currently I am using
subState :: (s1 -> s2) -> State s2 a -> State s1 s2 subState accessor f = do s <- get return $ execState f $ accessor s
to make the necessary transformation. I have the feeling that this way of stacking things is a bit inside-out. And as you say, it only hides the ugliness. I will try to stack things bottom up.
This turned out okay. Thank you very much and many thanks to Karl Voelker too, though I am still shying away from lenses.

On Oct 16, 2015, at 1:28 PM, martin
wrote: The expression to the right of <- must have the type
State System ItemDb
But ItemAdd' has the type
Item -> State ItemDb ()
You have an insight here which is quite general. If you have a monad State s, where s has a subcomponent of type t, you should be able to transform any action State t a into an “equivalent” action State s a, where instead of acting on the whole state, you act on the subcomponent. What would we need in order to do that transformation in a general way? A function to get the subcomponent out of the whole, and a function to put the new value of the subcomponent back into the whole. In other words, we’d need functions of types s -> t and t -> s -> s. Putting all of that together, we can imagine a transformation like this: f :: (s -> t) -> (t -> s -> s) -> State t a -> State s a I know of some libraries which provide a function with roughly that same type. The difference is that the first two parameters, the “getter” and “setter”, are combined into one parameter, and this combined thing is called a “lens”. Lenses are useful in all kinds of situations, and this just happens to be one of them. Of course, if you don’t already have a lens, any lens library will give you a way to build one out of the getter and setter functions. Anyway, the two library functions which provide this transformation are called focus (in the data-lens-fd package, which is used with the lens type defined in the data-lens package) [1] and zoom (in the lens package) [2]. The lens package is more powerful, but also a good deal more confusing, so I would start with data-lens and data-lens-fd if you want a gentle introduction to lenses. -Karl 1: http://hackage.haskell.org/package/data-lens-fd-2.0.5/docs/Data-Lens.html#v:... 2: http://hackage.haskell.org/package/lens-4.13/docs/Control-Lens-Zoom.html#v:z...
participants (3)
-
Grzegorz Milka
-
Karl Voelker
-
martin