2014-10-28 4 views
2

Можно ли написать функцию 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" 

ответ

8

Это не представляется возможным. Левая стрелка в синтаксисе шаблона рисунка в основном, не связанная с левой стрелкой в ​​обозначении.

Вы можете использовать новое расширение лямбда-случай, если вам нравится:

{-# 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" 
+2

LambdaCase вполне подходит для этого. –

+0

В итоге я выбрал Lambda case, спасибо –

+0

Поскольку все ветви начинаются с 'return', вы можете переписать его с помощью' (<&>) = flip fmap' as: 'получает счетчик <&> \ case 0 ->" hello "; 1 -> "bye". '(<&>)' из пакета объектива https://hackage.haskell.org/package/lens-4.13.2.1/docs/Control-Lens-Operators.html#v:-60--38--62- –

2

Почему бы не написать помощника?

pureA :: MyState -> String 
pureA (MyState 0) = "hello" 
pureA (MyState 1) = "bye" 
pureA _   = "" 

a :: State MyState String 
a = fmap doA get 

Это также следует за философией отделения проблем чистой логики от вашей нечистой логики.

+0

Как монашеская монашеская монада? – alternative

3

Нет. Здесь есть некоторые принципиальные концептуальные несоответствия.

шаблон соответствия работает только тогда, когда верхняя часть выражения является конструктора функции, но глава do -style блока будет нормальной функцией (в этом случае функция >>= определена в типовых категориях Monad).

гвардейской ожидают значение типа Bool, но значение, которое вы собираетесь передать им придется иметь тип State MyState Bool (так как одна из отличительных вещей монад является то, что вы не можете убежать от них). Так что охранники тоже никогда не будут работать.

Вы могут, но охват для экземпляр-функтор. Функторы определены в прелюдии; существует инфиксная форма fmap под названием <$> в Control.Applicative. Вы могли бы использовать это, говоря:

a' = process <$> gets counter 
    where 
     process 0 = "hello" 
     process _ = "bye" 

или делать то, что вы хотите с функцией process. Чтобы получить что-то большее, как >>=, вы также можете определить своего оператора как flip fmap, а затем вы можете написать, скажем, gets counter >= \x -> case x of ....

+1

Защитные щитки (стандартные с Haskell 2010) более мощные, чем обычные охранники, и им не нужно быть «Bool's». Однако они недостаточно сильны для этого случая использования. –

1

Да, это возможно, но я бы посоветовал вам не делать этого - трудно отслеживать, какая часть идет туда.

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 $ \ [email protected](MyState i) -> Identity $ 
    case i of 
    0 -> ("hello", s) 
    1 -> ("bye", s)