Answers:
我有点投机...
文化:我认为|>
是F#“文化”中的重要运算符,也许与.
Haskell 类似。F#具有函数组合运算符,<<
但我认为F#社区倾向于使用无点样式而不是Haskell社区。
语言差异:我对两种语言的了解还不够,但是也许使let-bindings泛化的规则足以影响这一点。例如,我知道在F#中有时会写
let f = exp
将不会编译,并且您需要显式的eta转换:
let f x = (exp) x // or x |> exp
使其编译。这也使人们远离无分/组合风格,而转向流水线风格。另外,F#类型推断有时需要进行流水线处理,以便在左侧显示一个已知类型(请参见此处)。
(就我个人而言,我发现无分的样式不可读,但我想所有新事物/不同事物在您习惯之前似乎都是不可读的。)
我认为这两种语言在两种语言中都可能可行,历史/文化/事故可能会定义为什么每个社区都定居于不同的“吸引人”。
.
和$
,因此人们继续使用它们。
(|>)
由于从左到右的类型检查,在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
被发现为止,并且可以解决该要求。但是,这确实使类型系统复杂化。
更多猜测,这次是从主要的Haskell方面开始的...
($)
是翻转 (|>)
,当您无法编写无点代码时,它的用法很常见。因此,(|>)
在Haskell 中不使用的主要原因是它的位置已经被($)
。
此外,从F#的一些经验来看,我认为(|>)
它在F#代码中非常受欢迎,因为它类似于Subject.Verb(Object)
OO结构。由于F#旨在实现平滑的功能/ OO集成,Subject |> Verb Object
因此对于新功能程序员而言,这是一个相当平滑的过渡。
就个人而言,我也喜欢从左至右思考,因此我(|>)
在Haskell中使用,但我认为没有其他人这样做。
Data.Sequence.|>
,但是$>
看起来很合理,可以避免在那里发生冲突。老实说,只有那么多漂亮的运算符,所以我将|>
同时使用这两种运算符,并逐案处理冲突。(而且,我也很想将别名Data.Sequence.|>
化为snoc
)
($)
并且(|>)
不是应用程序组成。两者是相关的(如问题注释所示),但它们是不相同的(您fc
是(Control.Arrow.>>>)
针对函数的)。
|>
实际上|
比其他任何东西更能使我想起UNIX 。
|>
F#的另一个好处是它具有Visual Studio中IntelliSense的出色属性。键入|>
,您会获得一个可以应用于左侧值的函数列表,类似于.
在对象后键入时发生的情况。
我认为我们很困惑。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
$
运算符-反转了一样,您也可以轻松地将其定义为:a |> b = flip ($)
它等同于F#的管道,例如,您可以这样做[1..10] |> map f
.
)与()相同<<
,而(>>
)是相反的组成。那就是( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
.
等于>>
。我不知道F#是否具有,<<
但是那是等效的(例如Elm)。
如果要|>
在Haskell中使用F#,则在Data.Function中是&
运算符(因为base 4.8.0.0
)。
&
了|>
?我觉得|>
它更加直观,这也让我想起了Unix管道运算符。
有些人在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]
有一个很好的理由有时会偏爱从左到右的构图:评估顺序遵循阅读顺序。
我认为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
IT支持 :
&
是Haskell的|>
。深埋在这个线程中,花了我几天时间才发现。我经常使用它,因为您自然会从左到右阅读以遵循您的代码。