Как наклеить матч на поле государственной монады?
Можно ли написать написать функцию a
используя паттены, соответствующие / охранники?
{-# 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} )
Я пытался писать a
как
a' :: State MyState String
a' | i <- gets counter, i == 0 = return "hello"
но получил ошибки:
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"
4 ответа
Это невозможно. Стрелка влево в синтаксисе охранника шаблона в основном не связана с стрелкой влево в нотации.
Вы можете использовать новое расширение lambda-case, если вам нравится:
{-# LANGUAGE LambdaCase #-}
a :: State MyState String
a = gets counter >>= \case
0 -> return "hello"
1 -> return "bye"
Или, может быть, многоходовой?
{-# LANGUAGE MultiWayIf #-}
a :: State MyState String
a = do
i <- gets counter
if
| i == 0 -> return "hello"
| i == 1 -> return "bye"
Нет. Здесь есть некоторые действительно фундаментальные концептуальные несоответствия.
Сопоставление с образцом работает только тогда, когда самая верхняя часть выражения является функцией конструктора, но заголовком do
-стиль блока будет нормальной функцией (в этом случае функция >>=
определяется в Typeclasss Monad
).
Охранники ожидают значения типа Bool
но значение, которое вы собираетесь передать им, должно быть типа State MyState Bool
(поскольку одна из отличительных черт монад в том, что от них невозможно убежать). Так что охранники тоже никогда не сработают.
Однако вы можете обратиться к экземпляру функтора. Функторы определены в Prelude; есть инфиксная форма fmap
называется <$>
в Control.Applicative
, Вы бы использовали это, сказав:
a' = process <$> gets counter
where
process 0 = "hello"
process _ = "bye"
или делать все, что вы хотите с process
функция. Чтобы получить что-то более похожее >>=
Вы также можете определить свой собственный оператор как flip fmap
и тогда вы можете написать, скажем, gets counter >= \x -> case x of ...
,
Почему бы не написать помощник?
pureA :: MyState -> String
pureA (MyState 0) = "hello"
pureA (MyState 1) = "bye"
pureA _ = ""
a :: State MyState String
a = fmap doA get
Это также следует философии отделения проблем чистой логики от вашей нечистой логики.
Да, это возможно, но я бы посоветовал вам не делать этого - трудно отследить, какая часть идет куда.
import Control.Monad.State.Strict(StateT(..))
import Data.Functor.Identity(Identity(..))
data MyState = MyState
{ counter :: Int
} deriving (Show)
a :: StateT MyState Identity String
a = StateT $ \ s@(MyState i) -> Identity $
case i of
0 -> ("hello", s)
1 -> ("bye", s)