105

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

4

10 回答 10

88

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

于 2009-09-22T10:12:12.277 回答
62

我有点投机...

Culture:我认为|>是 F#“文化”中的一个重要运算符,.对于 Haskell 来说可能也是如此。F# 有一个函数组合运算符,但我认为 F# 社区比 Haskell 社区更<<倾向于使用无点样式。

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

let f = exp

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

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

使其编译。这也引导人们远离无点/组合风格,而转向流水线风格。此外,F# 类型推断有时需要流水线,因此已知类型出现在左侧(请参阅此处)。

(就我个人而言,我发现无积分风格不可读,但我想每一个新的/不同的东西在你习惯之前似乎都是不可读的。)

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

于 2009-09-21T22:26:15.687 回答
46

更多的猜测,这一次主要来自 Haskell 方面......

($)是 的翻转(|>),当您无法编写无点代码时,它的使用非常普遍。所以在 Haskell 中没有使用的主要原因(|>)是它的位置已经被($).

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

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

于 2009-09-22T06:57:25.557 回答
33

我认为我们把事情搞糊涂了。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

于 2011-04-22T17:48:45.240 回答
30

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

于 2015-09-27T08:31:07.070 回答
17

我已经看到>>>被用于flip (.),而且我自己也经常使用它,尤其是对于从左到右最容易理解的长链。

>>>实际上来自 Control.Arrow,并且不仅仅适用于功能。

于 2009-10-02T00:35:47.753 回答
17

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]

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

于 2010-05-26T14:41:47.533 回答
15

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

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

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

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

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

于 2011-04-23T23:47:42.367 回答
13

我认为 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 (&)中的文档

中缀 = 中缀 + 左结合性

中缀 = 中缀 + 右结合性


(.)

( .) 表示功能组合。这意味着(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 —&gt; right这是和之间的不同right —&gt; 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
  • 斯卡拉:RxScala
  • Clojure:RxClojure
  • C++:RxCpp
  • Lua:RxLua
  • 红宝石:Rx.rb
  • 蟒蛇:RxPY
  • 去:RxGo
  • Groovy:RxGroovy
  • JRuby:RxJRuby
  • Kotlin:RxKotlin
  • 斯威夫特:RxSwift
  • PHP:RxPHP
  • 灵药:reaxive
  • 飞镖:RxDart
于 2019-05-15T08:34:36.257 回答
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 专家可以为您提供更好的解决方案。

于 2017-08-10T20:05:56.680 回答