在F#中,使用管道前向运算符|>
非常常见.但是,在Haskell中,我只见过使用过的函数组合(.)
.我知道它们是相关的,但是有没有一种语言原因在Haskell中没有使用管道转发,还是其他什么?
在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
看到并且可以解决此要求.但这确实使类型系统复杂化.
我有点投机......
文化:我认为|>
是F#"文化"中的重要运营商,也许与.
Haskell 类似.F#有一个函数组合运算符,<<
但我认为F#社区倾向于使用比Haskell社区更少的无点样式.
语言差异:我对两种语言的比较都知之甚少,但是推广let-bindings的规则可能会有很大的不同,从而影响到这一点.例如,我知道在F#中有时写作
let f = exp
不会编译,你需要明确的eta转换:
let f x = (exp) x // or x |> exp
使它编译.这也引导人们远离无点/组合风格,并走向流水线风格.此外,F#类型推断有时需要流水线操作,因此左侧会显示已知类型(请参见此处).
(就个人而言,我觉得无点的风格是不可读的,但我认为每一个新的/不同的东西似乎都不可读,直到你习惯了它.)
我认为两种语言都可能存在,历史/文化/事故可能会定义为什么每个社区都选择不同的"吸引者".
更多的猜测,这次来自Haskell主导方面......
($)
是你的翻版(|>)
,当你无法编写无点代码时,它的使用非常普遍.因此,(|>)
在Haskell 中没有使用的主要原因是它已经占据了它的位置($)
.
另外,从F#的一些经验来看,我认为(|>)
它在F#代码中非常流行,因为它类似于Subject.Verb(Object)
OO 的结构.由于F#旨在实现平滑的功能/ OO集成,Subject |> Verb Object
因此对于新的功能程序员来说是一个非常平滑的过渡.
就个人而言,我也喜欢从左到右思考,所以我(|>)
在Haskell中使用,但我不认为很多其他人会这样做.
我认为我们混淆了一些事情.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
如果你想|>
在Haskell中使用F#,那么在Data.Function中就是&
运算符(自base 4.8.0.0
).
有些人也在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]
有时候选择从左到右的构图是有充分理由的:评估顺序遵循阅读顺序.
我已经看到>>>
被用于flip (.)
,我经常使用它,特别是对于从左到右最好理解的长链.
>>>
实际上来自Control.Arrow,并且不仅仅是函数.
除了风格和文化之外,这归结为优化纯语言或不纯代码的语言设计.
该|>
运营商在F#中常见的主要是因为它有助于隐藏与主要-不纯的代码将出现两个限制:
从左到右的类型推断没有结构子类型.
价值限制.
注意,OCaml中不存在前一个限制,因为子类型是结构而不是名义,因此随着类型推断的进展,结构类型很容易通过统一来细化.
Haskell采取了不同的权衡,选择专注于主要纯粹的代码,可以解除这些限制.
我认为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 Function
或closure 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