Skip to content

Instantly share code, notes, and snippets.

@mengwong
Last active June 4, 2023 09:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mengwong/73af81ad600a533f12ef42fc655fed0f to your computer and use it in GitHub Desktop.
Save mengwong/73af81ad600a533f12ef42fc655fed0f to your computer and use it in GitHub Desktop.
evalRWST (RWST r w s (Either String) a) returns Either String (a, w) instead of (Either String a, w)
{-
This library demonstrates the difference between
RWST (Either String) a
RWS (Either String a)
The first form outputs
1 + 2, after bonus = Right (103,[])
5 + 10, after bonus = Right (115,["10 is a big number"])
12 + 19, after bonus = Right (131,["12 is a big number","19? I might run out of toes"])
12 + 30, after bonus = Left "30 is too big, I can't count that high"
But we would prefer it to output
(Right 103, [])
(Right 115, ["10 is a big number"])
(Right 131, ["12 is a big number","19? I might run out of toes"])
(Left "30 is too big, I can't count that high", ["12 is a big number"])
And that's what the second form does.
-}
module Lib
( someFunc
) where
import Data.Map as Map ( Map, (!), fromList )
import Control.Monad.RWS.Strict ( RWST, lift, evalRWST, asks, tell
, RWS, evalRWS )
import Control.Applicative (liftA2)
someFunc :: IO ()
someFunc = sequence_
[ putStrLn $ show i ++ " + " ++ show j ++ ", after bonus = " ++
show ( evalRWST
( myBonusT $ myAddT i j )
(fromList [("bonus", 100)])
mempty )
| (i, j) <- [ (1,2), (5,10), (12,19), (12,30) ]
]
>> someFunc2
type MyMT = RWST (Map String Int) [String] (Map String String) (Either String)
myAddT :: Int -> Int -> MyMT Int
myAddT x y = do
x' <- myValT x
y' <- myValT y
return (x' + y')
myValT :: Int -> MyMT Int
myValT n
| n < 10 = return n
| n < 15 = tell [show n ++ " is a big number"] >> return n
| n < 20 = mutterT (show n ++ "? I might run out of toes") >> return n
| otherwise = lift $ Left (show n ++ " is too big, I can't count that high")
mutterT :: String -> MyMT ()
mutterT s = tell [s]
myBonusT :: MyMT Int -> MyMT Int
myBonusT n = (+) <$> asks (! "bonus") <*> n
-- * Maybe having the Either String as a base monad for the transformer is too much.
-- Let's do another version as a plain RWS.
type MyM' a = RWS (Map String Int) [String] (Map String String) a
type MyM b = MyM' (Either String b)
someFunc2 :: IO ()
someFunc2 = sequence_
[ putStrLn $ show i ++ " + " ++ show j ++ ", after bonus = " ++
show ( evalRWS
( myBonus $ myAdd i j )
(fromList [("bonus", 100)])
mempty )
| (i, j) <- [ (1,2), (5,10), (12,19), (12,30) ]
]
myAdd :: Int -> Int -> MyM Int
myAdd x y = do
x' <- myVal x
y' <- myVal y
return ((+) <$> x' <*> y')
myVal :: Int -> MyM Int
myVal n
| n < 10 = return (Right n)
| n < 15 = tell [show n ++ " is a big number"] >> return (pure n)
| n < 20 = mutter (show n ++ "? I might run out of toes") >> pure (pure n)
| otherwise = pure $ Left (show n ++ " is too big, I can't count that high")
mutter :: String -> MyM' ()
mutter s = tell [s]
myBonus :: MyM Int -> MyM Int
myBonus n = do
n' <- n
b' <- Right <$> asks (! "bonus")
return ((+) <$> b' <*> n')
myBonus' :: MyM Int -> MyM Int
myBonus' n = liftA2 (+) <$> (Right <$> asks (! "bonus")) <*> n
@mengwong
Copy link
Author

mengwong commented Jun 4, 2023

Remark:

16:45 < ncf> freeside: the second form should be equivalent to ExceptT String RWS a, if you want to stick to monad transformers
16:54 < ncf> (and then you can rewrite your functions to work equally well with both versions, using mtl-style classes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment