State Monad和'put'函数在Haskell中

时间:2016-04-12 23:26:45

标签: haskell

The Documentation about the State Monad说:

put :: s -> m ()
     

替换monad中的状态。

我无法理解。这是否意味着函数替换内部 Monad?第二个问题:为什么返回值为m ()而不是m s

1 个答案:

答案 0 :(得分:6)

我认为,理解状态monad的最简单方法就是编写自己的状态并稍微使用它。研究这段代码,与其他人的例子一起玩,然后回来查看它,直到你能够从内存中写出来:

-- | 'State' is just a newtype wrapper around the type @s -> (a, s)@.
-- These are functions which are fed a state value (type @s@) as input,
-- and produce as a pair of an @a@ (the *result* of the state action)
-- and an @s@ (the *new state* after the action).
--
-- The 'State' type is fundamentally a shortcut for chaining functions
-- of types like that.
newtype State s a = State { runState :: s -> (a, s) }

instance Functor (State s) where
  fmap f (State g) = State $ \s0 -> 
      let (a, s1) = g s 
      in (f a, s1)

instance Applicative (State s) where
  pure a = State $ \s -> (a, s)
  State ff <*> State fa = State $ \s0 -> 
      let (s1, f) = ff s0
          (s2, a) = fa s1
      in (s2, f a)

instance Monad (State s) where
  return = pure
  State fa >>= f = State $ \s0 ->
      let (s1, a) = fa s0
          (s2, b) = runState (f a) s1
      in (s2, b)

-- | 'get' is just a wrapper around a function that takes the 
-- incoming @s@ value and exposes it in the position where @a@ 
-- normally goes.
get :: State s s
get = State $ \s -> (s, s)

-- | 'put' is a wrapper around a function that discards the
-- the incoming @s@ value and replaces it with another.
put :: s -> State s ()
put s = State $ \_ -> ((), s)

这是直接用State类型编写而不使用MonadState类,这一点起初要简单一些。作为练习,一旦您对此感到满意,您可以尝试使用MonadState课程进行编写。

  

第二个问题:为什么返回值为m ()而不是m s

据我所知,这主要是一种随意的设计选择。如果我正在设计State类型,我可能会像这样写getput,这与您的期望更相似:

-- | Modify the incoming state by applying the given function to it.
-- Produces the previous, now discarded state as a result, which is 
-- often useful.
modify :: (s -> s) -> State s s
modify f = State $ \s0 -> (s, f s)

-- Now 'get' and 'put' can be written in terms of 'modify':

get :: State s s
get = modify (\s -> s)

-- | This version of 'put' returns the original, discarded state,
-- which again is often useful.
put :: s -> State s s
put s = modify (\_ -> s)

如果你有标准的'get'和'put',你可以用它来编写我修改过的'put':

-- | 'get' the incoming state, 'put' a new one in, and 'return' the old one.
replace :: s -> State s s
replace s1 = do
  s0 <- get
  put s1
  return s0

无论如何,put产生()s是否会产生重大影响。