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

时间:2009-09-21 22:04:33

标签: haskell f# functional-programming composition

在F#中,使用管道转发运算符|>非常常见。但是,在Haskell中,我只看到过正在使用的函数组合(.)。我知道它们是related,但有没有语言原因在Haskell中没有使用管道转发,还是其他的呢?

10 个答案:

答案 0 :(得分:82)

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

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编写了proposal来为Haskell添加类似的名称解析,但他建议使用局部约束来跟踪类型是否支持特定操作。因此,在第一个示例中,x需要Value属性的要求将继续执行,直到看到xs并且可以解决此要求。但这确实使类型系统复杂化。

答案 1 :(得分:60)

我有点投机......

文化:我认为|>是F#“文化”中的重要运算符,也许与Haskell的.类似。 F#有一个函数组合运算符<<,但我认为F#社区比Haskell社区使用points-free style更少。

语言差异:我不太了解要比较的两种语言,但是推广let-bindings的规则可能会有很大不同,从而影响到这一点。例如,我知道在F#中有时写作

let f = exp

将无法编译,您需要明确的eta转换:

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

使其编译。这也引导人们远离无点/组合风格,并走向流水线风格。此外,F#类型推断有时需要流水线操作,因此左侧会显示已知类型(请参阅here)。

(就个人而言,我觉得无点的风格难以理解,但我认为每一个新的/不同的东西似乎都不可读,直到你习惯了它。)

我认为两种语言都可能存在,历史/文化/事故可能会定义为什么每个社区都选择不同的“吸引者”。

答案 2 :(得分:42)

更多猜测,这一次来自Haskell占主导地位......

($)(|>)的翻转,当你无法编写无点代码时,它的使用非常普遍。因此,(|>)未在Haskell中使用的主要原因是($)已经占据了它的位置。

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

就个人而言,我也喜欢从左到右思考,所以我在Haskell中使用(|>),但我认为很多其他人都没有。

答案 3 :(得分:30)

我认为我们混淆了事情。 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

答案 4 :(得分:19)

如果您想在Haskell中使用F#的|>,则Data.Function中的&运算符(自base 4.8.0.0起)。

答案 5 :(得分:16)

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]

有时候选择从左到右的构图是有充分理由的:评估顺序遵循阅读顺序。

答案 6 :(得分:15)

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

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

答案 7 :(得分:13)

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

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

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

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

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

答案 8 :(得分:1)

这是我尝试Haskell的第一天(在Rust和F#之后),我能够定义F#&#39; s |&gt;操作者:

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

答案 9 :(得分:0)

我认为 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 |>?请参阅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 ($)中定义。

. )用于创建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 —> rightright —> 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/

它支持:

  • 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
  • 科特琳:RxKotlin
  • Swift:RxSwift
  • PHP:RxPHP
  • 长生不老药:令人吃惊
  • Dart:RxDart
相关问题