将F#管道运算符(< |,>>,<<)转换为OCaml

时间:2013-01-19 03:56:32

标签: f# ocaml inline

我正在将一些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 ;;

4 个答案:

答案 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#程序员将要维护的生产代码很有价值。