Haskell组成(。)与F#的pipe道转发运算符(|>)

在F#中,使用pipe道转发运算符|>很常见。 然而,在Haskell中,我只见过函数组合(.)被使用。 我明白,他们是相关的 ,但有没有一个语言的原因,没有在Haskell中使用pipe道转发还是别的东西?

我有点投机…

文化 :我认为|>是F#”文化“中的一个重要的运营者,也许与之类似. 为Haskell。 F#有一个函数组合运算符<<但我认为F#社区倾向于使用比Haskell社区更less的无点风格 。

语言差异 :我不太了解两种语言进行比较,但是可能有关将let-bindings一般化的规则有很大的不同,以至于影响这一点。 例如,我知道在F#有时写作

 let f = exp 

不会编译,你需要明确的eta转换:

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

使其编译。 这也使得人们远离无点/构图风格,转向stream水线风格。 而且,F#types推理有时需要stream水线操作,以便在左侧出现一个已知types(请参见此处 )。

(就个人而言,我觉得无分的风格是不可读的,但是我认为每个新的/不同的东西在你习惯之前似乎是不可读的。)

我认为两种语言都可能是可行的,历史/文化/意外可能会解释为什么每个社区都有不同的“吸引力”。

在F# (|>)很重要,因为从左到右的types检查。 例如:

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

一般不会检查,因为即使xs的types是已知的, x.Value的参数x的types在typechecker看到它的时候是不知道的,所以它不知道如何parsingx.Value

相反

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

将会正常工作,因为xs的types将导致x的types被知道。

由于涉及像x.Value这样的结构的名称parsing,所以需要x.Value 。 西蒙·佩顿·琼斯(Simon Peyton Jones)已经写了一个向Haskell添加类似名称parsing的build议,但是他build议使用局部约束来跟踪一个types是否支持特定的操作。 因此,在第一个示例中, x需要一个Value属性的要求会一直持续到xs被发现,这个需求可以被解决。 不过这确实使types系统复杂化了。

更多的猜测,这次从主要的Haskell方面… …

($)(|>)的翻转,当你不能写无点代码时,它的使用是相当普遍的。 所以(|>)在Haskell中没有使用的主要原因是它的位置已经被($)占用了。

另外,从F#的经验来看,我认为(|>)在F#代码中非常stream行,因为它类似于OO的Subject.Verb(Object)结构。 由于F#旨在实现平滑的function/ OO集成,因此Subject |> Verb Object对新function程序员来说是一个非常平滑的过渡。

就我个人而言,我也喜欢从左到右思考,所以我在Haskell中使用(|>) ,但是我不认为其他人会这么做。

我认为我们混淆的东西。 Haskell的( . )等价于F#( >> )。 不要和F#的( |> )混淆,它只是反函数应用,就像Haskell的( $ ) – 颠倒过来:

 let (>>) fgx = g (fx) let (|>) xf = fx 

我相信Haskell程序员经常使用$ 。 也许不像F#程序员倾向于使用|>那样频繁。 另一方面,一些F#的人使用>>到一个荒谬的程度: http : //blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx

Haskell中的从左到右的组合

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

 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] 

有时有一个很好的理由喜欢从左到右的作文:评价顺序遵循阅读顺序。

我已经看到>>>被用于flip (.) ,我经常使用它自己,特别是从最左侧到右侧最好理解的长链。

>>>实际上来自Control.Arrow,并且不仅仅是函数。

如果你想在Haskell中使用F#的|> ,那么在Data.Function中就是&运算符(从base 4.8.0.0 )。

除了风格和文化,这可以归结为优化纯代码或不纯代码的语言devise。

|>操作符在F#中很常见,因为它有助于隐藏两个主要不纯代码出现的限制:

  • 从左到右的types推断,没有结构性的子types。
  • 价值限制。

注意前面的限制在OCaml中是不存在的,因为子types是结构化的而不是名义的,所以结构types可以通过types推断进行统一而容易地提炼出来。

Haskell采取了不同的权衡,select专注于主要的纯代码,这些限制可以被解除。

这是我第一次尝试Haskell(在Rust和F#之后),并且我能够定义F#的操作符:

 (|>) :: a -> (a -> b) -> b (|>) xf = fx infixl 0 |> 

它似乎工作:

 factorial x = case x of 1 -> 1 _ -> x * factorial (x-1) main = 5 |> factorial |> print 

我敢打赌,一个Haskell专家可以给你一个更好的解决scheme。