简体   繁体   中英

In composite StateT / Maybe monad, how to take either possibility that succeeds?

In Haskell, here's a monad that combines the State and Maybe monads:

type StatefulMaybe a = StateT Int Maybe a

This is a computation that can succeed (returning a value) or fail. If it succeeds, it carries a state along with the returned value.

I'd like to write a function

choice :: StatefulMaybe a -> StatefulMaybe a -> StatefulMaybe a

that takes two such computations and returns the first one (if any) that succeeds. Only state changes by the successful computation are carried forward.

In fact after some experimentation I figured out how to write this. Here it is:

orMaybe :: Maybe a -> Maybe a -> Maybe a
orMaybe (Just x) _ = Just x
orMaybe Nothing x = x

choice :: StatefulMaybe a -> StatefulMaybe a -> StatefulMaybe a
choice mx my = StateT (\s ->
    (runStateT mx s) `orMaybe` (runStateT my s)
  )

It works:

foo :: StatefulMaybe String
foo = do
    modify (+ 20)
    fail "didn't succeed"

baz :: StatefulMaybe String
baz = do
    modify (+ 30)
    return "two"

bar :: StatefulMaybe String
bar = do
    s <- choice foo baz
    return (s ++ " done")

> runStateT bar 0
Just ("two done",30)

My question is this: Is there some easier or more natural way to write this choice function than my implementation above? In particular, is there some way I can lift the orMaybe function into my monad?

If I understand the question correctly, I think that this function already exists: <|> :

bar :: StatefulMaybe String
bar = do
    s <- foo <|> baz
    return (s ++ " done")

The <|> function is part of the Applicative type class, of which StateT is an instance when the inner monad is both a Functor and MonadPlus instance, which is true for Maybe .

*Q66992923> runStateT bar 0
Just ("two done",30)

As luqui suggests in the comment, I think that mplus ought to work, too, since the default implementation of mplus is <|> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM