我正在将一些F#代码转换为OCaml,我看到这个管道运算符<|
有很多用途,例如:
let printPeg expr =
printfn "%s" <| pegToString expr
<|
运算符显然定义为:
# let ( <| ) a b = a b ;;
val ( <| ) : ('a -> 'b) -> 'a -> 'b = <fun>
我想知道为什么他们懒得在F#中定义和使用这个操作符,是不是就这样他们可以避免像这样放入parens?:
let printPeg expr =
Printf.printf "%s" ( pegToString expr )
据我所知,那就是将上面的F#代码转换为OCaml,对吗?
另外,我如何在Ocaml中实现F#的<<
和>>
运算符?
(|>
运算符似乎只是:let ( |> ) a b = b a ;;
)
答案 0 :(得分:15)
为什么他们懒得在F#中定义和使用这个操作符,是不是就这样他们可以避免放入parens?
这是因为编程的功能性假设通过一系列函数来线程化一个值。比较:
let f1 str server =
str
|> parseUserName
|> getUserByName server
|> validateLogin <| DateTime.Now
let f2 str server =
validateLogin(getUserByName(server, (parseUserName str)), DateTime.Now)
在第一个片段中,我们清楚地看到了价值所发生的一切。阅读第二篇文章,我们必须通过所有的事情来弄清楚发生了什么。
关于功能构成的This article似乎是相关的。
所以是的,在正常的生活中,主要是关于parens。而且,管道运营商与部分功能应用和无点编码风格密切相关。例如,请参阅Programming is "Pointless"。
管道|>
和函数组合>>
<<
运算符在传递给更高级别的函数时会产生另一个有趣的效果,例如here。
答案 1 :(得分:12)
直接来自F# source:
let inline (|>) x f = f x
let inline (||>) (x1,x2) f = f x1 x2
let inline (|||>) (x1,x2,x3) f = f x1 x2 x3
let inline (<|) f x = f x
let inline (<||) f (x1,x2) = f x1 x2
let inline (<|||) f (x1,x2,x3) = f x1 x2 x3
let inline (>>) f g x = g(f x)
let inline (<<) f g x = f(g x)
答案 2 :(得分:10)
OCaml Batteries支持这些运算符,但出于优先级,关联性和其他语法怪癖(如Camlp4)的原因,它使用不同的符号。最近刚刚解决了哪些特定符号,有一些变化。请参阅:Batteries API:
val (|>) : 'a -> ('a -> 'b) -> 'b
功能应用。 x |&gt; f等于f x。
val ( **> ) : ('a -> 'b) -> 'a -> 'b
功能应用。 f **&gt; x等于f x。 注意此运算符的名称不是一成不变的。它必将很快改变。
val (|-) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
功能组合。 f | - g很有趣x - &gt; g(f x)。这也相当于应用&lt; **两次。
val (-|) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
功能组合。 f - | g很有趣x - &gt; f(g x)。在数学上,这是算子o。
但是Batteries trunk提供了:
val ( @@ ) : ('a -> 'b) -> 'a -> 'b
功能应用。 [f @@ x]相当于[f x]。
val ( % ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
函数组合:数学[o]运算符。
val ( |> ) : 'a -> ('a -> 'b) -> 'b
“管道”:功能应用程序。 [x |&gt; f]相当于[f x]。
val ( %> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
管道功能组成。 [f%&gt; g]是[有趣的x - &gt; g(f x)]。
答案 3 :(得分:6)
我想知道为什么他们懒得在F#中定义和使用这个操作符,是不是就这样他们可以避免像这样放入这样的parens?
很好的问题。您所指的特定运算符(<|
)是非常无用的IME。它允许您在极少数情况下避免使用括号,但更一般地说,它通过拖动更多运算符使语法复杂化,并使经验较少的F#程序员(现在有很多)更难理解您的代码。所以我已经停止使用它了。
|>
运算符更有用,但仅仅是因为它有助于F#在OCaml不会出现问题的情况下正确推断类型。例如,这里有一些OCaml:
List.map (fun o -> o#foo) os
直接等效在F#中失败,因为在读取o
属性之前无法推断出foo
的类型,因此惯用解决方案是使用|>
重写这样的代码所以F#可以在使用o
之前推断出foo
的类型:
os |> List.map (fun o -> o.foo)
我很少使用其他运算符(<<
和>>
),因为它们也会使语法复杂化。我也不喜欢可以引入大量运算符的解析器组合库。
Bytebuster的例子非常有趣:
let f1 str server =
str
|> parseUserName
|> getUserByName server
|> validateLogin <| DateTime.Now
我会把它写成:
let f2 str server =
let userName = parseUserName str
let user = getUserByName server userName
validateLogin user DateTime.Now
我的代码中没有括号。我的临时工具有名称,所以它们出现在调试器中,我可以检查它们,当我将鼠标悬停在它们上面时,Intellisense可以给我类型回归。这些特性对于非专业F#程序员将要维护的生产代码很有价值。