【问题标题】:How to patten match on a field of a state monad?如何在状态单子的字段上进行模式匹配?
【发布时间】:2014-10-28 15:37:12
【问题描述】:

是否可以使用模式匹配/保护来编写函数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"

【问题讨论】:

    标签: haskell pattern-matching state-monad pattern-guards


    【解决方案1】:

    这是不可能的。模式保护语法中的左箭头大部分与 do-notation 中的左箭头无关。

    如果您愿意,可以使用新的 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"
    

    【讨论】:

    • LambdaCase 非常适合这个。
    • 由于所有分支都以return 开头,您可以使用(&lt;&amp;&gt;) = flip fmap 将其重写为:gets counter &lt;&amp;&gt; \case 0 -&gt; "hello"; 1 -&gt; "bye"。 (&lt;&amp;&gt;)来自镜头包hackage.haskell.org/package/lens-4.13.2.1/docs/…
    【解决方案2】:

    没有。这里有一些非常基本的概念不匹配。

    模式匹配仅在表达式的最顶部是 构造函数 函数时有效,但 do 样式块的头部将是 普通函数(在这种情况下,函数&gt;&gt;=在类型类Monad中定义)。

    Guards 期望一个 Bool 类型的值,但你要传递给他们的值必须是 State MyState Bool 类型(因为关于 monad 的独特之处之一是你无法逃脱他们)。所以守卫也永远不会工作。

    您可以但是可以使用 functor 实例。函子在 Prelude 中定义;在Control.Applicative 中有一个fmap 的中缀形式称为&lt;$&gt;。你可以这样说:

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

    或者使用process 函数做任何你想做的事情。要获得更像&gt;&gt;= 的内容,您还可以将自己的运算符定义为flip fmap,然后您可以写成gets counter &gt;= \x -&gt; case x of ...。

    【讨论】:

    • 模式守卫(Haskell 2010 以来的标准)比普通守卫更强大,不需要Bools。然而,它们对于这个用例来说还不够强大。
    【解决方案3】:

    为什么不写一个助手?

    pureA :: MyState -> String
    pureA (MyState 0) = "hello"
    pureA (MyState 1) = "bye"
    pureA _           = ""
    
    a :: State MyState String
    a = fmap doA get
    

    这也遵循将纯逻辑的关注点与不纯逻辑的关注点分开的哲学。

    【讨论】:

    • State monad 怎么不纯了?
    【解决方案4】:

    是的,这是可能的,但我建议你不要这样做 - 很难跟踪哪一块去哪里。

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-08
      • 2019-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      相关资源
      最近更新 更多