Reputation: 1593
I just found out about the PatternGuards language extension, which seems really nice.
I'm in a situation where I want to pattern match a value out, apply a monadic boolean function on that value, and only do something if the result is False
. Otherwise I want to execute a default action (that might not be as simple as listed below).
This can be achieved by using an if
after doing the pattern matching, but I would rather not duplicate the code for the default action, or move that out in a separate function (ie. keep the fall through behaviour of guards)
Is there a way to do this without the if
?
{-# LANGUAGE GeneralizedNewtypeDeriving, PatternGuards #-}
import Control.Applicative
import Control.Monad.State
newtype MyM a = MyM (StateT Int (Either String) a)
deriving ( MonadState Int
, Monad, Applicative, Functor
)
--------------------------------------------------------------------------------
foo :: Int -> MyM Bool
foo = undefined
bar :: Either a Int -> MyM Int
bar (Right n)
| ok <- foo n, ok == False = return 42
bar _ = return 0
The above code gives the error message
Couldn't match expected type ‘MyM Bool’ with actual type ‘Bool’
In the second argument of ‘(==)’, namely ‘False’
In the expression: ok == False
Upvotes: 4
Views: 1352
Reputation: 63359
What you're asking for is impossible, and for good reasons. Let's say that foo
modifies the state, but the condition in the guard doesn't match. How would you deal with this state modification? Patterns are separate from the values, so matching a pattern can't result in altering results.
You really need to have the call to foo
in the RHS that decides what to do with its output.
In some cases, where the monad is a data type you can match on, you can do things like
foo :: Int -> Maybe Bool
...
bar :: Either a Int -> MyM Int
bar (Right n)
| Just False <- foo n = ...
but only because you can match directly on the value of the monad.
Also in some cases extensions lambda-case and multi-way if-expressions can make your life somewhat easier.
Upvotes: 3