Search code examples
haskellpattern-matchingstate-monadpattern-guards

How to patten match on a field of a state monad?


Is it possible to write write the function a using pattens matching/guards?

{-# LANGUAGE PatternGuards #-}
import Control.Monad.State.Strict(State, gets, runStateT)
data MyState = MyState
    { counter :: Int
    } deriving (Show)


a :: State MyState String
a = do
    i <- gets counter
    case i of
        0 -> return "hello"
        1 -> return "bye"

run = runStateT a ( MyState{counter=0} )

I tried writing a as

a' :: State MyState String
a' | i <- gets counter, i == 0 = return "hello"

but got the errors:

No instance for (Control.Monad.State.Class.MonadState MyState m0)
  arising from a use of ‘gets’
The type variable ‘m0’ is ambiguous
Note: there are several potential instances:
  instance Control.Monad.State.Class.MonadState s m =>
           Control.Monad.State.Class.MonadState
             s (Control.Monad.Trans.Cont.ContT r m)
    -- Defined in ‘Control.Monad.State.Class’
  instance (Control.Monad.Trans.Error.Error e,
            Control.Monad.State.Class.MonadState s m) =>
           Control.Monad.State.Class.MonadState
             s (Control.Monad.Trans.Error.ErrorT e m)
    -- Defined in ‘Control.Monad.State.Class’
  instance Control.Monad.State.Class.MonadState s m =>
           Control.Monad.State.Class.MonadState
             s (Control.Monad.Trans.Except.ExceptT e m)
    -- Defined in ‘Control.Monad.State.Class’
  ...plus 12 others
In a stmt of a pattern guard for
               an equation for ‘a'’:
  i <- gets counter
In an equation for ‘a'’:
    a' | i <- gets counter, i == 0 = return "hello"

No instance for (Eq (m0 Int)) arising from a use of ‘==’
The type variable ‘m0’ is ambiguous
Relevant bindings include
  i :: m0 Int (bound at src/TestGen/Arbitrary/Helpers/Z.hs:18:6)
Note: there are several potential instances:
  instance Eq a => Eq (GHC.Real.Ratio a) -- Defined in ‘GHC.Real’
  instance (Eq e, Data.Functor.Classes.Eq1 m, Eq a) =>
           Eq (Control.Monad.Trans.Error.ErrorT e m a)
    -- Defined in ‘Control.Monad.Trans.Error’
  ...plus 118 others
In the expression: i == 0
In a stmt of a pattern guard for
               an equation for ‘a'’:
  i == 0
In an equation for ‘a'’:
    a' | i <- gets counter, i == 0 = return "hello"

Solution

  • This is not possible. Left arrow in the pattern guard syntax is mostly unrelated to the left arrow in the do-notation.

    You can use the new lambda-case extension if you like:

    {-# LANGUAGE LambdaCase #-}
    a :: State MyState String
    a = gets counter >>= \case
            0 -> return "hello"
            1 -> return "bye"
    

    Or multi-way if, perhaps?

    {-# LANGUAGE MultiWayIf #-}
    a :: State MyState String
    a = do
        i <- gets counter
        if
          | i == 0 -> return "hello"
          | i == 1 -> return "bye"