我目前正在努力应对Haskell的新元素:Monads。因此,我通过创建(>>=)
运算符的示例向我介绍了这个运算符,该运算符仅在Maybe
类型上执行函数(将其实际整数值作为其参数),只有它不相等到Nothing
,否则返回Nothing
:
(>>=) :: 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
值和一个返回一个的函数,但是在这个示例中,使用代码看起来像是它的两倍{ {1}}值和一次函数。但有人告诉我,它会评估Maybe
,将结果放在x
中,然后评估n
,将结果放入y
,然后执行y
两者兼顾。虽然我不知道safediv
运营商在这里扮演什么角色;这是如何运作的?
答案 0 :(得分: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
这只是(>>=)
eval x = Nothing
和eval y = Nothing
:
eval x >>= (...) = Nothing >>= (...) = Nothing
2. eval x = Nothing
和eval y = Just n
:
这是一样的:
eval x >>= (...) = Nothing >>= (...) = Nothing
3. eval x = Just n
和eval y = Nothing
:
eval x >>= (\n -> eval y >>= (...))
= Just n >>= (\n -> eval y >>= (...))
= Just n >>= (\n -> Nothing)
= Nothing
4. eval x = Just n
和eval 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
答案 1 :(得分: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
答案 2 :(得分:0)
你有
eval (Val n) = Just n
由此我们得出结论:eval
产生Maybe
值。第二个等式,让它重写为
eval (Div x y) =
eval x >>= (\n ->
eval y >>= (\m ->
safediv n m ) )
即
eval (Div x y) =
eval x >>= g
where
g n = eval y >>= h
where
h m = safediv n m
请参阅?每个>>=
应用程序中只涉及一个函数。在顶部,它是g
。但g
定义了 - 并使用 - h
,因此h
的正文可以访问其参数m
和g
&#39 ; s论点,n
。
如果eval x
生成Nothing
,那么根据eval x >>= g
类型的Nothing
定义,>>=
会立即Maybe
Nothing >>= _ = Nothing
),并且不会尝试eval y
。
但是如果它是(Just ...)
那么它的值只被馈送到绑定函数(Just x >>= f = f x
)。
因此,如果两个eval
生成Just ...
个值,则safediv n m
会在范围内调用,n
和m
都可以访问。它的可能定义为
safediv :: Num a => a -> a -> Maybe a
safediv n m | m == 0 = Nothing
| otherwise = Just (div n m) -- or something
所以h :: m -> Maybe m
和g :: 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