我试着学习箭头的含义,但我不理解它们。
我使用了维基图书教程。我认为Wikibook的问题主要是因为它似乎是为已经理解这个主题的人写的。
有没有人能解释一下箭头是什么以及我如何使用它们?
发布于 2012-10-20 14:48:39
我想补充的是,Haskell中的箭头比文献中显示的要简单得多。它们仅仅是函数的抽象。
要了解这是如何实际有用的,请考虑您有一组想要组合的函数,其中一些是纯函数,另一些是一元函数。例如,f :: a -> b
、g :: b -> m1 c
和h :: c -> m2 d
。
知道涉及的每个类型后,我可以手动构建一个组合,但组合的输出类型必须反映中间的monad类型(在上面的例子中,是m1 (m2 d)
)。如果我只想像对待a -> b
、b -> c
和c -> d
一样对待这些函数,那会怎么样呢?也就是说,我想抽象化单子的存在,只对底层类型进行推理。我可以使用箭头来准确地做到这一点。
这里有一个箭头,它抽象出了IO monad中函数的IO存在,这样我就可以用纯函数来组合它们,而不需要编写代码来知道涉及到IO。我们首先定义一个IOArrow来包装IO函数:
data IOArrow a b = IOArrow { runIOArrow :: a -> IO b }
instance Category IOArrow where
id = IOArrow return
IOArrow f . IOArrow g = IOArrow $ f <=< g
instance Arrow IOArrow where
arr f = IOArrow $ return . f
first (IOArrow f) = IOArrow $ \(a, c) -> do
x <- f a
return (x, c)
然后我制作了一些我想要组合的简单函数:
foo :: Int -> String
foo = show
bar :: String -> IO Int
bar = return . read
并使用它们:
main :: IO ()
main = do
let f = arr (++ "!") . arr foo . IOArrow bar . arr id
result <- runIOArrow f "123"
putStrLn result
这里我调用了IOArrow和runIOArrow,但是如果我在多态函数库中传递这些箭头,它们只需要接受"Arrow a => a b c“类型的参数。不需要让任何库代码知道涉及到monad。只有箭头的创建者和最终用户需要知道。
将IOArrow泛化为任何Monad中的函数都被称为“克莱斯利箭头”,并且已经有一个内置的箭头可以做到这一点:
main :: IO ()
main = do
let g = arr (++ "!") . arr foo . Kleisli bar . arr id
result <- runKleisli g "123"
putStrLn result
当然,您也可以使用箭头组合运算符和proc语法,以使涉及到的箭头更加清晰:
arrowUser :: Arrow a => a String String -> a String String
arrowUser f = proc x -> do
y <- f -< x
returnA -< y
main :: IO ()
main = do
let h = arr (++ "!")
<<< arr foo
<<< Kleisli bar
<<< arr id
result <- runKleisli (arrowUser h) "123"
putStrLn result
这里应该很清楚,尽管main
知道涉及到IO monad,但arrowUser
并不知道。如果没有箭头,就无法对arrowUser
“隐藏”IO --除非借助unsafePerformIO
将中间的一元值转换回纯值(从而永远失去该上下文)。例如:
arrowUser' :: (String -> String) -> String -> String
arrowUser' f x = f x
main' :: IO ()
main' = do
let h = (++ "!") . foo . unsafePerformIO . bar . id
result = arrowUser' h "123"
putStrLn result
试着在没有unsafePerformIO
和arrowUser'
必须处理任何Monad类型参数的情况下编写它。
发布于 2010-11-16 16:14:00
以下是John Hughes在AFP (高级函数式编程)研讨会上的讲稿。请注意,它们是在基库中更改Arrow类之前编写的:
发布于 2017-09-18 03:33:07
当我开始探索Arrow组合(本质上是Monad)时,我的方法是打破它最常与之相关的函数语法和组合,并从使用一种更具声明性的方法来理解其原则开始。考虑到这一点,我发现下面的分析更直观:
function(x) {
func1result = func1(x)
if(func1result == null) {
return null
} else {
func2result = func2(func1result)
if(func2result == null) {
return null
} else {
func3(func2result)
}
因此,本质上,对于某些值x
,首先调用一个函数,我们假设该函数可能返回null
(func1),另一个函数可能返回null
或互换地赋值给null
,最后调用第三个函数,该函数也可能返回null
。现在给定值x
,将x传递给func3,然后,如果它没有返回null
,则将该值传递给func2,并且只有当该值不为null时,才将该值传递给func1。它更具确定性,并且控制流允许您构造更复杂的异常处理。
这里我们可以使用箭头组合:(func3 <=< func2 <=< func1) x
。
https://stackoverflow.com/questions/4191424
复制相似问题