我可以看到你如何使用Monad是Haskell for IO--在这个操作的计算周围创建一个容器。有意义的是,你可以使用Monads“将计算一起插入” - 就像你为数据流操作组成函数一样。
我只是在做什么,你可以使用Monads进行控制流程。现在我理解控制流程是关于序列,选择和迭代。现在我对高阶函数(如map,foldl,filter和zipwith / mapcat)感到满意,以便对列表执行操作。
我的问题是 - 我可以使用monad进行序列,选择和迭代以实现控制流程吗? (很高兴在Haskell,Scala或Clojure中得到答案)
答案 0 :(得分:5)
对于Haskell中的排序,您有>>=
和sequence
函数:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
sequence :: Monad m => [m a] -> m [a]
>>=
或绑定函数采用monadic动作,从中提取值并将其提供给返回新monadic动作的函数。 sequence
函数获取相同类型的monadic动作列表并执行所有这些动作,聚合它们的结果并将其包装为单个动作。
对于迭代,您有mapM
和forM
(forM = flip mapM
)
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM
和forM
函数用于应用向列表中的每个元素返回操作的函数,将结果聚合为单个操作。
对于选择,我假设你的意思是条件,它在Haskell中实现为if-the-else表达式。它们可以直接在monadic表达式中使用,就像在纯表达式中使用它们一样。但是,您也可以使用某些monad执行选择或至少处理错误。最容易理解的是Maybe
monad:
data Maybe a = Nothing | Just a
instance Monad Maybe where
return a = Just a
(Just a) >>= f = f a
Nothing >>= f = Nothing
它的实现非常简单。基本上,如果您尝试将Nothing
排序为其他任何内容,则每次都会返回Nothing
。这为您提供了短路故障的概念:
lookup :: Eq a => a -> [(a, b)] -> Maybe b
-- Looks up a value in a key-value association list
myFunc :: Int -> [(String, Int)] -> Maybe Int
myFunc mult assocList = do
i <- lookup "foo" assocList
j <- lookup "bar" assocList
return $ i * mult + j
此处,如果"foo"
的查找失败,myFunc
会立即返回Nothing
。同样,如果"bar"
的查找失败,myFunc
会立即返回Nothing
。只有当两个查找都成功时myFunc
才进行任何计算。这提供了一种“错误处理”。有一个类似的monad Either a
data Either a b = Left a | Right b
instance Monad (Either a) where
return a = Right a
(Right a) >>= f = f a
(Left a) >>= f = Left a
除了“失败”值可以携带一些上下文之外,的工作方式非常相似,例如字符串错误消息或失败点的计算状态。