Fwd: Monad Transformers stack with variable depth

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Fwd: Monad Transformers stack with variable depth

leonidasbo .
Hello,

I am a beginner with Haskell and the last few days I am troubled on how to form a stack of Monad Transformers. 
The following example demonstrates the use of multiple State monads without the use of Monad Transformers:

data Item = Item { rCounter :: Int }

increaseCounter :: State Item ()
increaseCounter = do
  val <- gets rCounter
  modify (\i -> i { rCounter = val + 1 })

data ItemManager = ItemManager { rItems :: [Item] }

addItem :: Item -> State ItemManager ()
addItem item = do
  items <- gets rItems
  modify (\mgr -> mgr { rItems = items ++ [item] })

increaseCounterInItems :: State ItemManager ()
increaseCounterInItems = do
  items <- gets rItems
  let items' = map (execState increaseCounter) items
  modify (\mgr -> mgr { rItems = items' })

main :: IO ()
main = $ do
  let itemMgrState = do
        addItem $ Item 0
        addItem $ Item 10
        increaseCounterInItems

  let itemMgr = execState itemMgrState $ ItemManager []

  let items = rItems itemMgr
  putStrLn rCounter (items !! 0) -- prints 1
  putStrLn rCounter (items !! 1) -- prints 11

In the above implementation calls execState, inside functions of ItemManager, for updating Items. 
I have read about MonadTransformers and that they give you the ability to use monads inside the context of another monad.
So I would like to use StateT in signatures of ItemManager's functions and avoid calling execState, something like this:

increaseCounterInltems :: StateT ItemManager (State Item) ()

But I cannot find a way to implement the above function. The root cause of the difficulty seems to be that ItemManager contains multiple Items. 
The Monad Transformer stack contains a single State Item. So it seems to me that I am following the wrong approach.

How could I use Monad Transformers in order to avoid calling execState for modifying Items in ItemManager?

Thanks in advance,
Leonidas

Virus-free. www.avast.com


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: Monad Transformers stack with variable depth

Ivan Perez
Hi Leonidas

This is an interesting example. If I understand you correctly, what you want to do is to focus on one particular item, apply a (State) monadic computation that modifies it, replace that in the main list, as part of a larger monadic computation (also in a State monad / StateT transformer).

The first part, focusing on a sub-element, is fine. You could have an operation on a State monad that allows you to focus on a substate. But the problem is merging the result back into the main state: that would not happen automatically.

(Note for other readers: reminds me of lenses. Maybe StateT and Lens can be combined?)

It seems to me that, in this case, what you are already doing would be roughly the way to do it (if you want to use State also to modify the inner items).

Example of state transformers:

Imagine that you have an additional, distinguished Item, that you need to keep apart (apart is the keyword, not the fact that it's only one).

Then the monad you would be handling would be like:

State (ItemManager, Item)

No matter how many Items you have in the ItemManager (may be zero), you always have a distinguished Item separated.

But that means that, to manipulate this, you'd have to extract the ItemManager, modify it, put it back in the tuple, etc. So, you can also do:

StateT ItemManager (State Item)

Which means that you can modify ItemManager and Item independently, and manipulate both with:

op :: StateT ItemManager (State Item) ()
op = do
  modify f           -- computation that affects item manager
  lift (modify g)   -- computation that affects single item
  ...

You would then define your operations to be more flexible:

increaseCounterInItems :: StateT ItemManager m ()

So, you could also do:

op :: StateT ItemManager (State Item) ()
op = do
  increaseCounterIntItems     -- adds one to every Item in ItemManager
  lift increaseCounter          -- adds one to the single Item
  ...

Hope that helps

Best wishes

Ivan


On 6 March 2018 at 17:46, leonidasbo . <[hidden email]> wrote:
Hello,

I am a beginner with Haskell and the last few days I am troubled on how to form a stack of Monad Transformers. 
The following example demonstrates the use of multiple State monads without the use of Monad Transformers:

data Item = Item { rCounter :: Int }

increaseCounter :: State Item ()
increaseCounter = do
  val <- gets rCounter
  modify (\i -> i { rCounter = val + 1 })

data ItemManager = ItemManager { rItems :: [Item] }

addItem :: Item -> State ItemManager ()
addItem item = do
  items <- gets rItems
  modify (\mgr -> mgr { rItems = items ++ [item] })

increaseCounterInItems :: State ItemManager ()
increaseCounterInItems = do
  items <- gets rItems
  let items' = map (execState increaseCounter) items
  modify (\mgr -> mgr { rItems = items' })

main :: IO ()
main = $ do
  let itemMgrState = do
        addItem $ Item 0
        addItem $ Item 10
        increaseCounterInItems

  let itemMgr = execState itemMgrState $ ItemManager []

  let items = rItems itemMgr
  putStrLn rCounter (items !! 0) -- prints 1
  putStrLn rCounter (items !! 1) -- prints 11

In the above implementation calls execState, inside functions of ItemManager, for updating Items. 
I have read about MonadTransformers and that they give you the ability to use monads inside the context of another monad.
So I would like to use StateT in signatures of ItemManager's functions and avoid calling execState, something like this:

increaseCounterInltems :: StateT ItemManager (State Item) ()

But I cannot find a way to implement the above function. The root cause of the difficulty seems to be that ItemManager contains multiple Items. 
The Monad Transformer stack contains a single State Item. So it seems to me that I am following the wrong approach.

How could I use Monad Transformers in order to avoid calling execState for modifying Items in ItemManager?

Thanks in advance,
Leonidas

Virus-free. www.avast.com


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: Monad Transformers stack with variable depth

Oliver Charles-3


On Wed, Mar 7, 2018 at 11:42 AM Ivan Perez <[hidden email]> wrote:

(Note for other readers: reminds me of lenses. Maybe StateT and Lens can be combined?)

I've only skimmed the conversation, but I believe you're looking for zoom 

Ollie

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: Monad Transformers stack with variable depth

Brandon Allbery
Doesn't lens already have StateT support? The operators with a '=' in them.

On Wed, Mar 7, 2018 at 6:54 AM, Oliver Charles <[hidden email]> wrote:


On Wed, Mar 7, 2018 at 11:42 AM Ivan Perez <[hidden email]> wrote:

(Note for other readers: reminds me of lenses. Maybe StateT and Lens can be combined?)

I've only skimmed the conversation, but I believe you're looking for zoom 

Ollie

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.



--
brandon s allbery kf8nh                               sine nomine associates
[hidden email]                                  [hidden email]
unix, openafs, kerberos, infrastructure, xmonad        http://sinenomine.net

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.