(|>)
很重要,因为它支持从左到右的类型检查。例如:List.map (fun x -> x.Value) xs
一般不会进行类型检查,因为即使已知xs
的类型,当类型检查器看到lambda的参数x
时,它并不知道如何解析x.Value
,所以无法确定其类型。
相比之下,
xs |> List.map (fun x -> x.Value)
这将起作用,因为xs
的类型会导致x
的类型被识别。
从左到右的类型检查是必需的,因为涉及到像x.Value
这样的构造中的名称解析。Simon Peyton Jones已经提出了一个类似于Haskell的名称解析的建议,但他建议使用本地约束来跟踪类型是否支持特定操作。因此,在第一个示例中,需要x
具有Value
属性的要求将一直保留,直到看到xs
并且可以解决该要求。不过,这确实使类型系统变得更加复杂。
我有些推测...
文化: 我认为在F# "文化"中,|>
是一个重要的运算符,类似地,在Haskell中使用 .
。F# 有一个函数组合运算符<<
,但我认为 F# 社区不像 Haskell 社区那样经常使用点函式风格。
语言差异: 我不了解这两种语言足够多,无法比较它们之间的差异,也许用于一般化let绑定的规则是足够不同以至于会影响到这个问题。例如,我知道在 F# 中有时写
let f = exp
代码无法编译,需要进行显式 eta 转换:
let f x = (exp) x // or x |> exp
为了使其编译,这也使人们远离点式/组合风格,转向管道风格。此外,F#类型推断有时要求使用管道,以便已知类型出现在左侧(请参见此处)。
(个人认为,点式风格难以阅读,但我认为每一种新的/不同的东西都在你习惯之前难以阅读。)
我认为两种风格在任何语言中都有潜力可行,历史/文化/偶然性可能定义了为什么每个社区都定居在不同的“吸引点”上。
.
和$
,所以人们继续使用它们。 - Amok有更多来自以Haskell为主的推测......
($)
是 (|>)
的翻转版本,并且在无法编写点免代码时使用相当普遍。因此,(|>)
在Haskell中没有被使用的主要原因是它的位置已经被 ($)
占据了。
另外,从一些F#的经验来看,我认为 (|>)
在F#代码中如此流行,是因为它类似于面向对象的结构Subject.Verb(Object)
。由于F#旨在实现平滑的函数式/面向对象集成,Subject |> Verb Object
对于新的函数式编程人员来说是一个相当顺畅的过渡。
个人而言,我也喜欢从左到右思考,所以我在Haskell中使用 (|>)
,但我认为其他人并不会那么做。
Data.Sequence.|>
,但$>
看起来是避免冲突的合理选择。说实话,只有那么多好看的运算符,所以我会同时使用|>
来处理冲突的情况。(另外,我会想把Data.Sequence.|>
重命名为snoc
)。 - Nathan Shively-Sanders($)
和(|>)
是应用而不是组合。它们之间有关联(正如问题所指出的),但它们并不相同(对于函数,您的fc
是(Control.Arrow.>>>)
)。 - Nathan Shively-Sanders|>
符号让我想起了UNIX中的 |
更甚于其他任何东西。 - Kevin Cantu|>
的另一个好处是它在 Visual Studio 的 IntelliSense 中具有良好的属性。输入 |>
,您将获得可以应用于左侧值的函数列表,类似于在对象后键入 .
时发生的情况。 - Kristopher Johnson我觉得我们把事情搞混了。Haskell中的(.
)等同于F#中的(>>
)。不要与F#中的(|>
)混淆,后者只是反向函数应用,类似于Haskell中的($
) - 反转:
let (>>) f g x = g (f x)
let (|>) x f = f x
我相信Haskell程序员经常使用 $
。也许不像F#程序员经常使用 |>
那样频繁。另一方面,有些F#程序员过度使用 >>
,甚至到了荒唐的程度:http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx
$
运算符的反向操作。你也可以很容易地将其定义为:a |> b = flip ($)
, 这等同于 F# 的管道操作。例如,你可以使用 [1..10] |> map f
的方式来使用它。请注意不要改变原文意思,并尽可能使翻译通俗易懂。 - vis.
) 和 (<<
) 的意思相同,而 (>>
) 则是反向组合。即 ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
与 ( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
。 - Dobes Vandermeer.
不等同于 >>
。我不知道 F# 是否有 <<
,但那将是等效的(就像 Elm 中一样)。 - Matt Joiner&
而不是 |>
的原因是什么?我觉得 |>
更直观,而且它也让我想起了 Unix 管道操作符。 - yeshengm&
很直观。只需将 &
发音为 "and",代码几乎可以用英语正确地读出来。例如:5 & factorial & show
可以朗读为 "take 5 and then take factorial of it and then apply show to it"。 - Marcel Besixdouze有些人在Haskell中也使用左到右(消息传递)风格。例如,在Hackage上可以看到mps库的示例:
euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum
我认为在某些情况下这种样式看起来很好,但是它更难以阅读(需要了解库及其所有运算符,重新定义的(.)
也很困扰)。
Control.Category中还有从左到右和从右到左的组合运算符,这是base包的一部分。 分别比较>>>
和<<<
:
ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]
有时候有充分的理由偏爱从左到右的组合:计算顺序遵循阅读顺序。
我认为在Haskell中,F#的管道正向运算符(|>
)应该被使用,而不是(&)。
// pipe operator example in haskell
factorial :: (Eq a, Num a) => a -> a
factorial x =
case x of
1 -> 1
_ -> x * factorial (x-1)
// terminal
ghic >> 5 & factorial & show
如果你不喜欢(&
)运算符,你可以像F#或Elixir一样自定义它:
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 1 |>
ghci>> 5 |> factorial |> show
为什么要用 infixl 1 |>
?请参见Data-Function (&)的文档。
infixl
= 带左结合性的中缀运算符
infixr
= 带右结合性的中缀运算符
(.
) 表示函数组合。在数学上它表示(f.g)(x) = f(g(x))。
foo = negate . (*3)
// ouput -3
ghci>> foo 1
// ouput -15
ghci>> foo 5
它等于
// (1)
foo x = negate (x * 3)
或者// (2)
foo x = negate $ x * 3
($
) 运算符在Data-Function ($)中也有定义。
(.
) 用于创建高阶函数或JavaScript中的闭包。请参见示例:
// (1) use lamda expression to create a Hight Order Function
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
// (2) use . operator to create a Hight Order Function
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
哇,更简洁的代码更好。
|>
和 .
ghci> 5 |> factorial |> show
// equals
ghci> (show . factorial) 5
// equals
ghci> show . factorial $ 5
这是左到右和右到左之间的不同。⊙﹏⊙|||
|>
和 &
比 .
更好。
因为
ghci> sum (replicate 5 (max 6.7 8.9))
// equals
ghci> 8.9 & max 6.7 & replicate 5 & sum
// equals
ghci> 8.9 |> max 6.7 |> replicate 5 |> sum
// equals
ghci> (sum . replicate 5 . max 6.7) 8.9
// equals
ghci> sum . replicate 5 . max 6.7 $ 8.9
它支持以下语言:
我看到过在 flip (.)
中使用 >>>
,并且我经常自己使用它,特别是对于最好从左到右理解的长链。
>>>
实际上来自 Control.Arrow,并且可以作用于不只是函数。
>>>
在 Control.Category
中被定义。 - Steven Shaw(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>
而且它似乎能够工作:
factorial x =
case x of
1 -> 1
_ -> x * factorial (x-1)
main =
5 |> factorial |> print
我打赌一个Haskell专家能给你一个更好的解决方案。
infix
:)x |> f = f x
- Jamie
&
是 Haskell 中的|>
。这个信息藏在这个帖子深处,让我花费了几天时间才找到。我经常使用它,因为你自然地从左向右阅读代码。 - itmuckel