4

我正在绞尽脑汁想了解如何将Statemonad 与Maybe.

让我们从一个具体的(并且故意微不足道/不必要的)示例开始,在该示例中,我们使用Statemonad 来查找数字列表的总和:

import Control.Monad.State

list :: [Int]
list = [1,4,5,6,7,0,3,2,1]

adder :: Int
adder = evalState addState list

addState :: State [Int] Int
addState = do
  ms <- get
  case ms of
    []     -> return 0
    (x:xs) -> put xs >> fmap (+x) addState

凉爽的。

现在让我们对其进行修改,使其Nothing在列表包含数字时返回 a 0。换句话说,evalState addState' list应该返回Nothing(因为list包含 a 0)。我以为它可能看起来像这样......

addState' :: State [Int] (Maybe Int)
addState' = do
  ms <- get
  case ms of
    [] -> return (Just 0)
    (0:xs) -> return Nothing
    (x:xs) -> put xs >> fmap (fmap (+x)) addState'

...它有效,但我认为有更好的方法来做到这一点...

我玩过StateTMaybeT但我无法让他们工作。我看过一些关于 Monad 变换器的介绍,但它们要么没有涉及这个特定的组合(即 State + Maybe),要么示例太复杂以至于我无法理解。

TL;DR:StateT如果有人可以展示如何使用and MaybeT(两个示例)编写这段(诚然微不足道的)代码,我将不胜感激。(我假设不使用转换器就不可能编写此代码 - 这是不正确的吗?)

PS我的理解是,这StateT可能更适合这个例子,但从概念上看这两个例子会很有帮助,如果不是太麻烦的话。

更新:正如@Brenton Alker 所指出的,我上面的代码的第一个版本由于简单的错字(我缺少一个撇号)而不起作用。为了将问题集中在StateT/的使用上MaybeT,我正在更正上面的帖子。只是想包含这个注释来为他的帖子提供背景信息。

4

3 回答 3

8

我推荐使用的类型是:

StateT [Int] Maybe Int

使用Maybe/的一个非常简单的方法是在您想要失败以及想要从失败的计算中恢复时MaybeT调用。即使它们在其他 monad 转换器中分层,这也有效。mzeromplus

这是一个例子:

addState' :: StateT [Int] Maybe Int
addState' = do
  ms <- get
  case ms of
    []     -> return 0
    (0:xs) -> mzero
    (x:xs) -> put xs >> fmap (fmap (+x)) addState

-- This requires generalizing the type of `addState` to:
addState :: Monad m => StateT [Int] m Int

请注意,我以没有使用任何Maybe特定操作的方式编写了它。事实上,如果你让编译器推断类型签名,它会推断出这个更通用的类型:

addState' :: MonadPlus m => StateT [Int] m Int

这是有效的,因为StateT有以下MonadPlus实例:

instance MonadPlus m => MonadPlus (StateT s m) where ...

并且Maybe会作为 的实例进行类型检查MonadPlus,这就是为什么当我们专注mMaybe.

于 2014-11-24T02:16:47.853 回答
1

我相信您的解决方案基本上是正确的,您只是有一些小问题。

  1. 您的递归调用addState缺少素数 - 即。应该是addState'(鉴于报告的错误,我怀疑这只是粘贴问题的一个问题)
  2. 您正在断言adder :: Int,但在新版本中应该是adder :: Maybe Int- 我认为这是您遇到的类型错误。

不幸的是,我目前没有资源来尝试变形金刚版本。

于 2014-11-24T02:17:29.663 回答
0

此示例使用MaybeT (State [Int]) Int.

我们有一个Monad,它State包含(a Stack) 和(只是你取出/放入 Stack 的元素,或者当你试图从空堆栈中弹出一些东西时什么都没有)。s -> (a, s)s :: [Int]a :: Maybe Int

-- -----------------------------------------
-- -----------------------------------------
import Control.Monad.Trans.State
import Control.Monad.Trans.Maybe

-- -----------------------------------------
-- -----------------------------------------
pop2 :: MaybeT (State [Int]) Int
pop2 = MaybeT . state $ \xx -> case xx of
  [] -> (Nothing, [])
  x:xs -> (Just x, xs)

push2 :: Int -> MaybeT (State [Int]) Int
push2 x =  MaybeT . state $ \xs -> (Just x, x:xs)

-- -----------------------------------------
-- -----------------------------------------
dup2 = do
  x <- pop2
  push2 x
  push2 x

-- -----------------------------------------
-- -----------------------------------------
main = do
  print $ runState (runMaybeT dup2) [12, 34, 56]
  print $ runState (runMaybeT dup2) []

当我们运行程序时,我们得到:

(Just 12,[12,12,34,56]) 
(Nothing,[])

让我们回顾一下类型:

起初,

MaybeT :: m (Maybe a) -> MaybeT m am包含 a 的单子Maybe a

我们使用m == State [Int]a == Int

所以

MaybeT :: (State [Int]) (Maybe Int) -> MaybeT (State [Int]) Int

runMaybeT :: MaybeT m a -> m (Maybe a) == MaybeT (State [Int]) Int -> (State [Int]) (Maybe Int)runMaybeT取出 MaybeT 所附的内容)。

runState of (State [Int]) (Maybe Int) == [Int] -> ((Maybe Int), [Int])runState拉出(State [Int]) (Maybe Int)附件)。

于 2018-10-04T06:06:30.417 回答