Haskell:(也许)monad构建
我目前正在用Haskell的一个新元素Monads来挣扎。 因此,我通过创建一个>> =运算符的例子来介绍这一点,该运算符在可能类型上执行一个函数(仅将其实际整数值作为它的参数),只要它不等于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的函数,但是在这个示例中,使用代码看起来好像它需要2次可能值和一次函数。 然而,我被告知它评估x,将结果放入n,然后评估y,将结果放入y中,然后对两者执行safediv函数。 虽然我没有看到(>> =)运算符在这里扮演什么角色, 这个怎么用?
如果有人能帮我解决这个问题,我会非常感激。
提前致谢!
最好的问候,Skyfe。
你可以这样读取它:
eval (Div x y) = eval x >>= (n ->
eval y >>= (m ->
safediv n m))
当你想做eval (Div xy)
eval x
: Just n
(使用第一个>> = ) n
并看看eval y
(使用第一个>> = ) Just m
(第二>> = ) m
做一个(第二>> = ) savediv nm
返回它的结果-你仍然有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 = 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
让我们来追逐元素来说明它是如何工作的。 如果我们有
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
你有
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
看到? y >>= h
在g
内完成,在其参数n
的范围内。 由eval x >>= g
生成的n
是(Just some_n) >>= g
。
如果eval x
产生Nothing
,那么eval x >>= g
只是Nothing
,立即根据>>=
定义为Maybe
的类型( Nothing >>= _ = Nothing
),以及没有eval y
将尝试。
但是,如果它是(Just ...)
那么它的值就会被馈送给绑定函数( (Just x) >>= f = fx
)。
因此,如果两个eval
s都生成Just ...
值,则safediv nm
将在可以访问参数n
和m
的范围内调用。
我想它被定义为
safediv n m | m == 0 = Nothing
| otherwise = Just (div n m) -- or something
所以h :: m -> Maybe m
和g :: n -> Maybe n
和类型符合(>>=) :: (Maybe a) ->
(a -> Maybe b)
-> (Maybe b)
。