2

我目前正在努力解决 Haskell 的一个新元素:Monads。因此,我通过创建一个运算符的示例来介绍这一点,该运算符仅在不等于时才(>>=)对类型执行函数(将其实际整数值作为参数) ,否则返回:MaybeNothingNothing

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= _ = Nothing
(Just x) >>= f = f x

但是,我不太确定它的以下用法如何工作:

eval (Val n) = Just n
eval (Div x y) = eval x >>= (\n ->
    eval y >>= (\m ->
        safediv n m))

在我看来,(>>=)运算符只需要一个Maybe值和一个返回一个值的函数,但是在此示例使用代码中,它似乎需要 2 次Maybe值和一次函数。但是有人告诉我,它会评估x,将结果放入n,然后评估y,将结果放入y,然后safediv在两者上执行函数。虽然我看不到(>>=)运营商在这里是如何发挥作用的;这是如何运作的?

4

3 回答 3

6

你可以这样读:

eval (Div x y) = eval x >>= (\n ->
    eval y >>= (\m ->
        safediv n m))

当你想做的eval (Div x y)时候

  • 首先eval x
    • 如果是Just n(使用第一个>>=
    • 然后n看一下eval y(使用第一个>>=
      • 如果最后一个是Just m(第二个>>=
      • 然后采取m并做一个(第二个>>=
      • savediv n m返回它的结果 - 你仍然有n你的关闭!

在其他情况下返回Nothing

所以这里(>>=)只是帮助你解构。

也许以以下形式更容易阅读和理解do

eval (Val n) = Just n
eval (Div x y) = do
    n <- eval x
    m <- eval y
    safediv n m

这只是句法糖(>>=)

让我们追逐案例:

1.eval x = Nothingeval y = Nothing
eval x >>= (...) = Nothing >>= (...) = Nothing
2.eval x = Nothingeval y = Just n

这是一样的:

eval x >>= (...) = Nothing >>= (...) = Nothing
3.eval x = Just neval y = Nothing
eval x >>= (\n -> eval y >>= (...))
= Just n >>= (\n -> eval y >>= (...)) 
= Just n >>= (\n -> Nothing)
= Nothing
4.eval x = Just neval y = Just m
eval x >>= (\n -> Just m >>= (...))
= Just n >>= (\n -> Just m >>= (...)) 
= Just n >>= (\n -> Just m >>= (\m -> safediv n m))
= (first >>= for Just) = Just m >>= (\n -> safediv n m)
= (second >>= for Just) = safediv n m
于 2014-10-07T16:17:14.913 回答
1

让我们做元素追逐来说明它是如何工作的。如果我们有

eval (Div (Val 5) (Div (Val 0) (Val 1)))

然后我们可以把它分解成

eval (Div (Val 5) (Div (Val 0) (Val 1)))
    = eval (Val 5) >>=
        (\n ->
            eval (Div (Val 0) (Val 1)) >>=
                (\m ->
                    safediv n m
                )
        )

-- eval (Val 5) = Just 5

    = Just 5 >>=
        (\n ->
            eval (Div (Val 0) (Val 1)) >>=
                (\m ->
                    safediv n m
                )
        )

-- Just x >>= f = f x

    = (\n ->
        eval (Div (Val 0) (Val 1)) >>=
            (\m ->
                safediv n m
            )
      ) 5

-- Substitute n = 5, since the 5 is the argument to the `\n ->` lamba

    = eval (Div (Val 0) (Val 1)) >>=
        (\m ->
            safediv 5 m
        )

现在我们需要绕道计算eval (Div (Val 0) (Val 1))...

eval (Div (Val 0) (Val 1))
    = eval (Val 0) >>=
        (\n ->
            eval (Val 1) >>=
                (\m ->
                    safediv n m
                )
        )

-- eval (Val 0) = Just 0
-- eval (Val 1) = Just 1

eval (Div (Val 0) (Val 1))
    = Just 0 >>=
        (\n ->
            Just 1 >>=
                (\m ->
                    safediv n m
                )
        )

-- Just x >>= f = f x

eval (Div (Val 0) (Val 1))
    = (\n ->
        (\m ->
            safediv n m
        ) 1
      ) 0

    = (\n -> safediv n 1) 0
    = safediv 0 1
    = Just 0

现在回到我们最初对 的调用eval,替换Just 0为:

eval (Div (Val 5) (Div (Val 0) (Val 1)))
    = Just 0 >>= (\m -> safediv 5 m)

-- Just x >>= f = f x

eval (Div (Val 5) (Div (Val 0) (Val 1)))
    = safediv 5 0

-- safediv x 0 = Nothing

eval (Div (Val 5) (Div (Val 0) (Val 1)))
    = Nothing
于 2014-10-07T16:36:44.717 回答
0

你有

eval (Val n) = Just n

由此我们得出结论,eval产生一个Maybe价值。第二个等式,我们把它改写为

eval (Div x y) = 
  eval x >>= (\n ->
                    eval y >>= (\m ->
                                      safediv n m ) )

IE

eval (Div x y) = 
  eval x >>= g 
             where
             g n =  eval y >>= h 
                               where
                               h m =  safediv n m

看?每个应用程序只涉及一个>>=功能。在顶部,它是g. 但是g定义 - 并使用 - h,所以h's 的主体可以访问它的参数 mg' 的参数,n

如果eval x产生Nothing,则eval x >>= g根据Nothing类型( ) 的>>=定义立即为,并且不会尝试。MaybeNothing >>= _ = Nothingeval y

但如果是,(Just ...)那么它的值只会被馈送到绑定函数 ( Just x >>= f = f x)。

因此,如果两个evals 都产生Just ...值,则在两个参数和均可访问safediv n m的范围内调用。它可能被定义为nm

safediv :: Num a => a -> a -> Maybe a
safediv n m | m == 0    =  Nothing
            | otherwise =  Just (div n m)    -- or something

等等h :: m -> Maybe mg :: n -> Maybe n适合的类型,

-- assuming a missing type of "expressions", `Exp a`,
eval :: Num a => Exp a ->                                       Maybe a    
  -- Num a is assumed throughout, below
  eval (Div x y) =                                           -- Maybe a
  -- Maybe a >>= a ->                                           Maybe a
      eval x >>= g 
                 where
  --               a ->                                         Maybe a
  --                   Maybe a >>= a ->                         Maybe a 
                 g n =  eval y >>= h
                                   where
  --                                 a ->                       Maybe a
                                   h m =  safediv    n    m  -- Maybe a
  --                                      safediv :: a -> a ->  Maybe a

根据 Maybe monad 的绑定类型,

(>>=) :: Maybe a -> 
              (a -> Maybe b) -> 
         Maybe            b
于 2014-10-07T17:26:56.000 回答