2014-10-28 87 views
2

有沒有可能寫使用模式匹配/守衛功能a如何在狀態monad的字段上匹配匹配?

{-# 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

這是不可能的。模式保護語法中的左箭頭是,主要是,與do-notation中的左箭頭無關。

您可以使用新的λ-情況下擴展,如果你喜歡:如果

{-# 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對此非常好。 – 2014-10-28 17:20:51

+0

我最終選擇了Lambda案例,謝謝 – 2014-10-28 23:53:55

+0

由於所有分支都以'return'開頭,因此您可以用'(<&>)= flip fmap'將其改寫爲:'gets counter <&> \ case 0 - >「hello」; 1 - >「再見」。 '(<&>)'來自鏡頭包https://hackage.haskell.org/package/lens-4.13.2.1/docs/Control-Lens-Operators.html#v:-60--38--62- – 2016-03-15 00:26:22

2

爲什麼不寫一個幫手?

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

a :: State MyState String 
a = fmap doA get 

這也遵循了從你的不純邏輯中分離純邏輯問題的哲學。

+0

'State' monad如何不純? – alternative 2014-10-29 00:34:39

3

不,這裏有一些非常基本的概念錯配。

模式匹配當表達的最上部是一個構造功能,但do風格塊的頭部將是一個正常功能(在這種情況下所定義的函數>>=只能在類型類Monad)中。

衛隊期望Bool類型的值,但你要交出他們將不得不State MyState Bool類型的(因爲大約單子與衆不同的事情之一是,你不能從他們逃脫)的值。所以警衛也永遠不會工作。

可以但是達到仿函數實例。 Functor在Prelude中定義;在Control.Applicative中有fmap的中綴形式<$>。你會說這樣做:

a' = process <$> gets counter 
    where 
     process 0 = "hello" 
     process _ = "bye" 
任何你想用 process功能

或做。要獲得更類似>>=的內容,您還可以將自己的運營商定義爲flip fmap,然後您可以編寫例如gets counter >= \x -> case x of ...

+1

模式守衛(自Haskell 2010以來的標準)比普通守衛更強大,並且不需要成爲'布爾'。然而,它們對於這個用例來說不夠強大。 – 2014-10-29 00:11:34

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)