RasmusWL
RasmusWL

Reputation: 1593

Using a monadic boolean value in a guard? (PatternGuard)

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

Answers (1)

Petr
Petr

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

Related Questions