You don’t even need monads here — you can rewrite ugly using fmap:
ugly = fmap $ decode . fromStrict
At which point you can even inline it into getHelloWorldR:
getHelloWorldR :: Handler Html
getHelloWorldR = do
myObjText <- lookupSessionBS "myobj" :: Handler (Maybe ByteString) -- gets serialized MyObj from a session cookie, or Nothing
myObj <- return $ fmap (decode . fromStrict) myObjText :: Handler (Maybe MyObj)
Or you could even use the <$> operator, which is an infix version of fmap:
getHelloWorldR :: Handler Html
getHelloWorldR = do
myObjText <- lookupSessionBS "myobj" :: Handler (Maybe ByteString) -- gets serialized MyObj from a session cookie, or Nothing
myObj <- return $ (decode . fromStrict) <$> myObjText :: Handler (Maybe MyObj)
And in fact, since you’re running return and then immediately assigning it via <-, it can be replaced with a let:
getHelloWorldR :: Handler Html
getHelloWorldR = do
myObjText <- lookupSessionBS "myobj" :: Handler (Maybe ByteString) -- gets serialized MyObj from a session cookie, or Nothing
let myObj :: Maybe MyObj
myObj = (decode . fromStrict) <$> myObjText
But I would also like to present a slightly different view on monads to that in your question. Monads aren’t just a tool to remove pattern matching — they’re a very general abstraction over the concept of running things in sequence. For instance:
- When using
Maybe, ‘running’ a computation consists of checking to see whether it’s Nothing, aborting the computation if this happens, otherwise continuing on. (This corresponds to a computation which can fail.)
- When using a list, ‘running’ a computation consists of splitting the computation into multiple parts, one for each element in the list, then joining those parts back again. (This corresponds to a nondeterministic computation.)
- When using
IO, ‘running’ a computation is executing it on a computer.
- When using
Handler, ‘running’ a computation is interacting with the request sent and/or responding with something.
So you shouldn’t reach for monads every time you want to manipulate a Maybe — on the contrary, it’s often much easier to do this without monads, as I showed above! Monads only become useful when you need to sequence things. In your example, this is the case with Handler — because you need to sequence multiple interactions with the request — but is not useful with Maybe, because you just need to apply a series of operations to a value which happens to be wrapped inside a Maybe.
ugly :: ByteString -> MyObj; thenfmap ugly :: Maybe ByteString -> Maybe MyObj. - chepnerMaybe MyObj. - BloodySue>>=expects strange types of function and returns strange types" you need to dwell on it until those stop looking strange to you. also check out the>=>Kleisli composition operator. Monad Laws are easier to grasp with it. "Monad axioms: Kleisli composition forms a category". - Will Ness