我写的是一个真正的 monad 吗?

Is what I wrote an actual monad?

我一直在努力理解 monad,自从我最近理解了什么是拉链后,我想我可能会尝试将这两种想法结合起来。 (>>=) 做了我认为 monad 应该做的事情,即它让我以 moveRight >>= moveLeft >>= goAhead >>= return 的形式结合拉链周围的运动,但我觉得我错过了一些东西,因为除其他外,我不能它的类型似乎符合 monad 应该是什么,即 Ma -> (a -> Mb) -> Mb。欢迎任何帮助。

module MonadZipper where

import Prelude hiding (return, (>>=))

data Node a = Fork a (Node a) (Node a)
            | Passage a (Node a)
            | DeadEnd a
              deriving (Show)

data Branch a = TurnLeft a (Node a)
              | TurnRight a (Node a)
              | StraightAhead a
                deriving (Show)

type Trace a = [Branch a]
type Zipper a = (Trace a, Node a)


type Movement a = Zipper a -> Maybe (Zipper a)
--not sure whether this wrapping makes sense

turnLeft :: Zipper a -> Maybe (Zipper a)
turnLeft (t, (Fork v l r)) = Just (TurnLeft v r:t, l)
turnLeft _                 = Nothing

turnRight :: Zipper a -> Maybe (Zipper a)
turnRight (t, (Fork v l r)) = Just (TurnRight v l:t, r)
turnRight _                 = Nothing

goAhead :: Zipper a -> Maybe (Zipper a)
goAhead (t, Passage v a) = Just (StraightAhead v:t, a)
goAhead _                = Nothing

(>>=) :: Movement a -> Movement a -> Movement a
(>>=) turner func = \zippo ->
                      case turner zippo of
                        Nothing -> Nothing
                        Just tree -> func tree

return :: Zipper a -> Maybe (Zipper a)
return tree = Just tree

您的 Movement 类型很像 Maybe monad(允许失败的移动)加上 State monad 与当前 Zipper a 的组合州:

State (Zipper a) b  =  Zipper a -> (b, Zipper a)

我在用 = 作弊。这不是 State 类型的精确定义,但这些类型是同构的,因此您可以认为 State 等于此类型。

换句话说,您已经接近重新发明基于 transformer 的 monad:

type Movement' a b = StateT (Zipper a) Maybe b

主要区别在于 Movement' a b 同构于:

Zipper a -> Maybe (b, Zipper a)

因此它具有您未包含的额外 b 值。

太棒了....

如果您要将 Movement 类型重写为:

type Movement a b = Zipper a -> Maybe (b, Zipper a)

你会有所作为的。这里,Movement 不是 monad——相反,Movement a 是一个可以应用于基础类型 Movement a b.

的 monad

如果您熟悉 Either 作为 monad,那是一回事:Either 本身不是 monad,但 Either String 是一个可以应用于 Either String Double 之类的另一种类型,以表示 return 是 Double 结果或 String 错误消息的计算。

同样,您的 Movement a 是一个单子,可以应用于另一种类型 Movement a b 以表示 return 是 b 的计算,同时保持 Zipper a 作为内部状态并允许通过 returning Nothing.

失败

继续,您的 turnLeftturnRightgoAhead 是纯效果:它们修改状态(monad 的 State 部分)、信号错误如果进行了不可能的移动(monad 的 Maybe 部分),但他们不需要 return 任何东西。没关系,因为他们可以 return ()goAhead 的工作原理如下:

goAhead :: Movement a ()
-- same as:  goAhead :: Zipper a -> Maybe ((), Zipper a)
goAhead (t, Passage v a) = Just ((), (StraightAhead v:t, a))
goAhead _                = Nothing

并且您可以对 turnLeftturnRight 进行类似的更改。

现在,重新定义 return 相对容易。它应该将 b 类型的任意值打包到你的 Movement a monad 中,而不需要任何 "effects"。看看你能不能填空:

return :: b -> Movement a b
-- same as:  return :: b -> Zipper a -> Maybe (b, Zipper a)
-- in definitino below, right hand side should be:
--     Movement a b = Zipper a -> Maybe (b, Zipper a)
return b = \z -> _

当然,(>>=)稍微难一点。看看你能不能想出来:

(>>=) :: Movement a b -> (b -> Movement a c) -> Movement a c
-- in definition below, right-hand side is a:
--   Movement a c = Zipper a -> Maybe (b, Zipper a)
mb >>= bToMc = \z1 -> case mb z1 of ...

如果您放弃,我已经在下面提供了答案。

有了这个 monad,事情会变得更有趣。例如,您可以引入一个 return 某事的动作。有效动作集怎么样?

data Move = LeftOk | RightOk | StraightOk

validMoves :: Movement a [Move]
validMoves z@(t, n) = case n of
  (Fork _ _ _)  -> Just ([LeftOk, RightOk], z)
  (Passage _ _) -> Just ([StraightOk], z)
  (DeadEnd _)   -> Just ([], z)

或者当前位置的元素:

peek :: Movement a a
peek z@(_, n) = case n of
  Fork a _ _  -> Just (a, z)
  Passage a _ -> Just (a, z)
  DeadEnd a   -> Just (a, z)

使用它,您可以构建一个走拉链的单子动作,始终使用第一个有效移动,并且 returns 是死胡同处的值:

findDeadEnd :: Movement a a
findDeadEnd =
    validMoves >>= \moves ->
    case moves of [] -> peek
                  (mv:_) -> (case mv of StraightOk -> goAhead
                                        LeftOk     -> turnLeft
                                        RightOk    -> turnRight)
                            >>= \() -> findDeadEnd

如果这是一个真正的 monad 实例,您可以用 do 表示法更清楚地编写上面的内容。

还不错吧?

无论如何,包含 return>>= 答案的完整代码如下。接下来,您可能想尝试将 Movement 包装成一个新类型,以便您可以定义实例:

newtype Movement a b 
  = Movement { runMovement :: Zipper a -> Maybe (b, Zipper a) }
instance Functor (Movement a) where
instance Applicative (Movement a) where
instance Monad (Movement a) where

看看你是否可以重写所有内容以使其成为真实的 Monad

完整示例:

module MonadZipper where

import Prelude hiding (return, (>>=))

data Node a = Fork a (Node a) (Node a)
            | Passage a (Node a)
            | DeadEnd a
              deriving (Show)

data Branch a = TurnLeft a (Node a)
              | TurnRight a (Node a)
              | StraightAhead a
                deriving (Show)

type Trace a = [Branch a]
type Zipper a = (Trace a, Node a)

type Movement a b = Zipper a -> Maybe (b, Zipper a)

(>>=) :: Movement a b -> (b -> Movement a c) -> Movement a c
mb >>= bToMc = \z1 -> case mb z1 of
                        Nothing -> Nothing
                        Just (b, z2) -> bToMc b z2

return :: b -> Movement a b
return b z = Just (b, z)

turnLeft :: Movement a ()
turnLeft (t, (Fork v l r)) = Just ((), (TurnLeft v r:t, l))
turnLeft _                 = Nothing

turnRight :: Movement a ()
turnRight (t, (Fork v l r)) = Just ((), (TurnRight v l:t, r))
turnRight _                 = Nothing

goAhead :: Movement a ()
goAhead (t, Passage v a) = Just ((), (StraightAhead v:t, a))
goAhead _                = Nothing

data Move = LeftOk | RightOk | StraightOk

validMoves :: Movement a [Move]
validMoves z@(t, n) = case n of
  (Fork _ _ _)  -> Just ([LeftOk, RightOk], z)
  (Passage _ _) -> Just ([StraightOk], z)
  (DeadEnd _)   -> Just ([], z)

peek :: Movement a a
peek z@(_, n) = case n of
  Fork a _ _  -> Just (a, z)
  Passage a _ -> Just (a, z)
  DeadEnd a   -> Just (a, z)

findDeadEnd :: Movement a a
findDeadEnd =
    validMoves >>= \moves ->
    case moves of [] -> peek
                  (mv:_) -> (case mv of StraightOk -> goAhead
                                        LeftOk     -> turnLeft
                                        RightOk    -> turnRight)
                            >>= \() -> findDeadEnd

test = case findDeadEnd ([], (Fork 1 (Fork 2 (Passage 3 (DeadEnd 4))
                                             (DeadEnd 5))
                                     (Passage 6 (DeadEnd 7)))) of
         Just (v, _) -> print v