简体   繁体   English

也许monad建设

[英]Maybe monad construction

I'm currently struggling with a new element of Haskell: Monads. 我目前正在努力寻找Haskell的新元素:单子。 Therefore I was introduced to this by an example of creating a (>>=) operator that executes a function on a Maybe type (taking its actual integer value as argument to it) only if it's not equal to Nothing , and otherwise return Nothing : 因此,我通过创建一个(>>=)运算符的例子向我介绍了这个运算符,该运算符在Maybe类型上执行一个函数(将其实际的整数值作为参数),只有它不等于Nothing ,否则返回Nothing

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

However, I'm not quite sure how this works with the following usage of it: 但是,我不太确定它在以下用法下如何工作:

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

It seems to me that the (>>=) operator simply takes one Maybe value and a function that returns one, however in this example usage code it seems like it's taking 2 times a Maybe value and once a function. 在我看来, (>>=)运算符只需要一个Maybe值和一个返回一个的函数,但是在这个示例中,使用代码看起来像是一个Maybe值和一次函数。 I was told however that it evaluates x , puts the result in n , then evaluates y , puts the result in y , and then executes the safediv function on both. 但是我被告知,它将对x求值,将结果safediv n ,然后对y求值,将结果safediv y ,然后对两者都执行safediv函数。 Although I don't see how the (>>=) operator plays its role here; 尽管我在这里看不到(>>=)运算符如何发挥作用; How does this work? 这是如何运作的?

You can read it like this: 您可以这样阅读:

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

when you want do eval (Div xy) then 当你想做eval (Div xy)然后

  • first eval x : 第一次eval x
    • if was Just n (using the first >>= ) 如果是Just n (使用第一个>> =
    • then take the n and have a look at eval y (using the first >>= ) 然后取n并看看eval y (使用第一个>> =
      • if the last is Just m (second >>= ) 如果最后一个是Just m (第二个>> =
      • then take the m and do a (second >>= ) 然后拿m并做一个(第二个>> =
      • savediv nm to return it's result - you still have the n from your closure!. savediv nm返回它的结果 - 你仍然有你的关闭n

in ever other caes return Nothing 在以往其他CAES回到Nothing

So here the (>>=) just helps you to deconstruct. 所以这里(>>=)只是帮助你解构。

Maybe it's easier to read and understand in the do form: 也许在do表单中阅读和理解更容易:

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

which is just syntactic sugar around (>>=) 这只是语法糖(>>=)

let's chase the cases: 让我们来追逐案例:

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

which is just the same: 这是一样的:

eval x >>= (...) = Nothing >>= (...) = Nothing
3. eval x = Just n and eval y = 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 n and eval y = Just m : 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

Let's do element chasing to illustrate how it works. 让我们做元素追逐来说明它是如何工作的。 If we have 如果我们有

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

Then we can break this down into 然后我们可以将其分解为

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
        )

Now we need to take a detour to compute eval (Div (Val 0) (Val 1)) ... 现在我们需要绕道计算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

And now back to our original call to eval , substituting Just 0 in: 现在回到我们最初的调用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

you have 你有

eval (Val n) = Just n

from this we conclude that eval produces a Maybe value. 由此我们得出结论, eval产生一个Maybe值。 The second equation, let's rewrite it as 第二个等式,我们将其重写为

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

See? 看到? There is only one function involved in each >>= application. 每个>>=应用程序仅涉及一个功能。 At the top, it's g . 在顶部,是g But g defines – and uses – h , so h 's body has access both to its argument m and the g 's argument, n . 但是g定义并使用了h ,因此h的主体可以访问其参数mg的参数n

If eval x produced Nothing , then eval x >>= g is just Nothing , immediately, according to the >>= definition for the Maybe types ( Nothing >>= _ = Nothing ), and no eval y will be attempted. 如果eval x产生Nothing ,则根据Maybe类型的>>=定义( Nothing >>= _ = Nothing ),立即eval x >>= g只是Nothing ,并且不会尝试eval y

But if it was (Just ...) then its value is just fed to the bound function ( Just x >>= f = fx ). 但是,如果它是(Just ...)那么它的值将被馈送到绑定函数中( Just x >>= f = fx )。

So if both eval s produce Just ... values, safediv nm is called inside the scope where both arguments n and m are accessible. 因此,如果两个eval产生Just ...值,则在可访问参数nm的范围内调用safediv nm It's probably defined as 可能被定义为

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

and so h :: m -> Maybe m and g :: n -> Maybe n and the types fit, 所以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

as per the type of bind for the Maybe monad, 根据Maybe monad的绑定类型,

(>>=) :: Maybe a -> 
              (a -> Maybe b) -> 
         Maybe            b

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM