Haskell合成(。)与F#的管道前导运算符(|>)


100

在F#中,通常使用管道前移运算符|>。但是,在Haskell中,我只见过使用了函数组合(.)。我知道它们是相关的,但是是否有语言上的原因,Haskell中未使用管道转发,还是其他原因?


2
lihanseys回答&是Haskell的|>。深埋在这个线程中,花了我几天时间才发现。我经常使用它,因为您自然会从左到右阅读以遵循您的代码。
itmuckel

Answers:


61

我有点投机...

文化:我认为|>是F#“文化”中的重要运算符,也许与.Haskell 类似。F#具有函数组合运算符,<<但我认为F#社区倾向于使用无点样式而不是Haskell社区。

语言差异:我对两种语言的了解还不够,但是也许使let-bindings泛化的规则足以影响这一点。例如,我知道在F#中有时会写

let f = exp

将不会编译,并且您需要显式的eta转换:

let f x = (exp) x   // or x |> exp

使其编译。这也使人们远离无分/组合风格,而转向流水线风格。另外,F#类型推断有时需要进行流水线处理,以便在左侧显示一个已知类型(请参见此处)。

(就我个人而言,我发现无分的样式不可读,但我想所有新事物/不同事物在您习惯之前似乎都是不可读的。)

我认为这两种语言在两种语言中都可能可行,历史/文化/事故可能会定义为什么每个社区都定居于不同的“吸引人”。


7
我同意文化差异。传统的Haskell使用.$,因此人们继续使用它们。
Amok

9
点的自由有时比点的更具可读性,有时则更少。我通常在map和filter之类的函数的参数中使用它,以避免lambda使事情混乱。我有时也会在顶级函数中使用它,但使用频率较低,并且仅在其简单易用时使用。
Paul Johnson

2
我认为其中没有太多的文化意义,就F#而言,在这个问题上根本没有太多选择(出于您和Ganesh提到的原因)。所以我想说两者在Haskell中都是可行的,但是F#绝对可以更好地使用管道运算符。
Kurt Schelfthout,2009年

2
是的,从左到右阅读的文化:)即使是数学也应该这样教……
nicolas

1
从左到右的@nicolas是一个任意选择。您可以习惯从右到左。
马丁·卡波迪奇

86

(|>)由于从左到右的类型检查,在F#中很重要。例如:

List.map (fun x -> x.Value) xs

通常不会进行类型检查,因为即使的类型xs已知,类型检查器x看到的时候lambda 参数的类型也不知道,因此它不知道如何解析x.Value

相反

xs |> List.map (fun x -> x.Value)

会正常工作,因为的类型xs会导致x已知。

由于像这样的结构涉及名称解析,因此需要从左到右的类型检查x.Value。西蒙·佩顿·琼斯(Simon Peyton Jones)提出了为Haskell添加类似类型的名称解析的建议,但他建议使用局部约束来跟踪类型是否支持特定操作。因此,在第一个样本中,x需要保留某个Value属性的要求,直到xs被发现为止,并且可以解决该要求。但是,这确实使类型系统复杂化。


1
有趣的是,Haskell中有(<|)类似于(。)的运算符,其数据的方向从右到左。但是如何进行类型解析呢?
The_Ghost

7
(<|)实际上类似于Haskell的($)。从左到右的类型检查仅用于解决.Value之类的问题,因此(<|)在其他情况下或使用显式类型注释时效果很好。
GS-向Monica致谢,2009年

44

更多猜测,这次是从主要的Haskell方面开始的...

($) 是翻转 (|>),当您无法编写无点代码时,它的用法很常见。因此,(|>)在Haskell 中不使用的主要原因是它的位置已经被($)

此外,从F#的一些经验来看,我认为(|>)它在F#代码中非常受欢迎,因为它类似于Subject.Verb(Object) OO结构。由于F#旨在实现平滑的功能/ OO集成,Subject |> Verb Object因此对于新功能程序员而言,这是一个相当平滑的过渡。

就个人而言,我也喜欢从左至右思考,因此我(|>)在Haskell中使用,但我认为没有其他人这样做。


嗨,内森,在Haskell平台的任何位置都预定义了“翻转($)”吗?名称“(|>)”已在Data.Sequence中定义,具有另一种含义。如果尚未定义,您怎么称呼它?我正在考虑使用“(($>)= flip($)”)
mattbh 2010年

2
@mattbh:不是我可以通过Hoogle找到它。我不知道Data.Sequence.|>,但是$>看起来很合理,可以避免在那里发生冲突。老实说,只有那么多漂亮的运算符,所以我将|>同时使用这两种运算符,并逐案处理冲突。(而且,我也很想将别名Data.Sequence.|>化为snoc
Nathan Shively-Sanders 2010年

2
($)并且(|>)不是应用程序组成。两者是相关的(如问题注释所示),但它们是不相同的(您fc(Control.Arrow.>>>)针对函数的)。
Nathan Shively-Sanders

11
实际上,F#|>实际上|比其他任何东西更能使我想起UNIX 。
凯文·坎图

3
|>F#的另一个好处是它具有Visual Studio中IntelliSense的出色属性。键入|>,您会获得一个可以应用于左侧值的函数列表,类似于.在对象后键入时发生的情况。
克里斯托弗·约翰逊

32

我认为我们很困惑。Haskell的(.)等同于F#的(>>)。不要与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


2
正如您说的那样,就像Haskell的$运算符-反转了一样,您也可以轻松地将其定义为:a |> b = flip ($)它等同于F#的管道,例如,您可以这样做[1..10] |> map f
相对于2012年

8
我认为(.)与()相同<<,而(>>)是相反的组成。那就是( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
Dobes Vandermeer 2013年

-1为“荒谬地使用>>”。(嗯,我没有,但是你明白我的意思)。F#中的F表示“功能”,因此功能组合是合法的。
Florian F

1
当然,我同意功能组合是合法的。我指的是“一些F#家伙”!那是我自己的博客。:P
AshleyF '16

我认为这不.等于>>。我不知道F#是否具有,<<但是那是等效的(例如Elm)。
马特·乔纳

28

如果要|>在Haskell中使用F#,则在Data.Function中&运算符(因为base 4.8.0.0)。


任何理由Haskell的选择&|>?我觉得|>它更加直观,这也让我想起了Unix管道运算符。
yeshengm

16

Haskell中从左到右的构图

有些人在Haskell中也使用从左到右(消息传递)样式。见,例如,MPS上Hackage库。一个例子:

euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum

我认为这种样式在某些情况下看起来不错,但是很难阅读(需要了解库及其所有运算符,重新定义(.)也很麻烦)。

基本包的一部分Control.Category中也有从左到右以及从右到左的组合运算符。分别比较>>><<<

ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]

有一个很好的理由有时会偏爱从左到右的构图:评估顺序遵循阅读顺序。


很好,所以(>>>)在很大程度上等同于(|>)?
肯佐

@Khanzor不完全是。(|>)应用参数,(>>>)主要是函数组合(或类似的东西)。然后,我想有一些固定性差异(没有检查)。
sastanin 2012年

15

我见过>>>被用来flip (.),并且我经常自己使用它,尤其是对于最好从左到右理解的长链。

>>> 实际上是从Control.Arrow来的,并且不仅仅在函数上工作。


>>>在中定义Control.Category
史蒂文·肖

13

除了样式和文化之外,这归结为针对纯代码或不纯代码优化语言设计。

|>运营商在F#中常见的主要是因为它有助于隐藏与主要-不纯的代码将出现两个限制:

  • 没有结构子类型的从左到右的类型推断。
  • 值限制。

请注意,在OCaml中不存在以前的限制,因为子类型化是结构性的而不是名义性的,因此随着类型推断的进行,结构化类型很容易通过统一来完善。

Haskell采取了另一种折衷方案,选择专注于可以消除这些限制的主要纯代码。


8

我认为F#的管道前移运算符(|>)应该与haskell中的对决。

// 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 |>呢 请参阅数据功能(&)中的文档

infixl =中缀+左联想

infixr =中缀+右关联


(。)

.)表示功能组成。在数学中表示(fg)(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($)中定义

.)用于创建Hight Order Functionclosure in js。参见示例:


// (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 

left —> right和之间是不同的right —> left。⊙﹏⊙|||

人性化

|>并且&.

因为

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

如何使用面向对象的语言进行功能编程?

请访问http://reactivex.io/

IT支持 :

  • Java:RxJava
  • JavaScript:RxJS
  • C#:Rx.NET
  • C#(统一):UniRx
  • Scala:RxScala
  • Clojure:RxClojure
  • C ++:RxCpp
  • Lua:RxLua
  • Ruby:Rx.rb
  • Python:RxPY
  • 转到:RxGo
  • Groovy:RxGroovy
  • JRuby:RxJRuby
  • Kotlin:RxKotlin
  • 斯威夫特:RxSwift
  • PHP:RxPHP
  • 长生不老药
  • 飞镖:RxDart

1

这是我尝试Haskell的第一天(在Rust和F#之后),我能够定义F#的|>运算符:

(|>) :: 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专家可以为您提供更好的解决方案。


1
您还可以定义中缀运算符infix :) x |> f = f x
Jamie
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.