首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >什么是箭头,如何使用它们?

什么是箭头,如何使用它们?
EN

Stack Overflow用户
提问于 2010-11-16 13:20:20
回答 3查看 13.3K关注 0票数 77

我试着学习箭头的含义,但我不理解它们。

我使用了维基图书教程。我认为Wikibook的问题主要是因为它似乎是为已经理解这个主题的人写的。

有没有人能解释一下箭头是什么以及我如何使用它们?

EN

回答 3

Stack Overflow用户

发布于 2012-10-20 14:48:39

我想补充的是,Haskell中的箭头比文献中显示的要简单得多。它们仅仅是函数的抽象。

要了解这是如何实际有用的,请考虑您有一组想要组合的函数,其中一些是纯函数,另一些是一元函数。例如,f :: a -> bg :: b -> m1 ch :: c -> m2 d

知道涉及的每个类型后,我可以手动构建一个组合,但组合的输出类型必须反映中间的monad类型(在上面的例子中,是m1 (m2 d))。如果我只想像对待a -> bb -> cc -> d一样对待这些函数,那会怎么样呢?也就是说,我想抽象化单子的存在,只对底层类型进行推理。我可以使用箭头来准确地做到这一点。

这里有一个箭头,它抽象出了IO monad中函数的IO存在,这样我就可以用纯函数来组合它们,而不需要编写代码来知道涉及到IO。我们首先定义一个IOArrow来包装IO函数:

代码语言:javascript
运行
复制
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)

然后我制作了一些我想要组合的简单函数:

代码语言:javascript
运行
复制
foo :: Int -> String
foo = show

bar :: String -> IO Int
bar = return . read

并使用它们:

代码语言:javascript
运行
复制
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中的函数都被称为“克莱斯利箭头”,并且已经有一个内置的箭头可以做到这一点:

代码语言:javascript
运行
复制
main :: IO ()
main = do
  let g = arr (++ "!") . arr foo . Kleisli bar . arr id
  result <- runKleisli g "123"
  putStrLn result

当然,您也可以使用箭头组合运算符和proc语法,以使涉及到的箭头更加清晰:

代码语言:javascript
运行
复制
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将中间的一元值转换回纯值(从而永远失去该上下文)。例如:

代码语言:javascript
运行
复制
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

试着在没有unsafePerformIOarrowUser'必须处理任何Monad类型参数的情况下编写它。

票数 34
EN

Stack Overflow用户

发布于 2010-11-16 16:14:00

以下是John Hughes在AFP (高级函数式编程)研讨会上的讲稿。请注意,它们是在基库中更改Arrow类之前编写的:

http://www.cse.chalmers.se/~rjmh/afp-arrows.pdf

票数 2
EN

Stack Overflow用户

发布于 2017-09-18 03:33:07

当我开始探索Arrow组合(本质上是Monad)时,我的方法是打破它最常与之相关的函数语法和组合,并从使用一种更具声明性的方法来理解其原则开始。考虑到这一点,我发现下面的分析更直观:

代码语言:javascript
运行
复制
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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4191424

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档