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

10

我正在将一些 F# 代码转换为 OCaml,我看到有很多使用这个管道操作符<|的情况,例如:

let printPeg expr =
    printfn "%s" <| pegToString expr
< p > 这个 <| 运算符似乎仅仅定义为:

# let ( <| ) a b = a b ;;
val ( <| ) : ('a -> 'b) -> 'a -> 'b = <fun>
我在想为什么他们要在F#中定义并使用这个运算符,是为了避免像这样放括号吗?

我在想为什么在F#中定义和使用这个运算符,是为了避免像这样使用括号吗?

let printPeg expr =
    Printf.printf "%s" ( pegToString expr )

就我所知,那将是将上面的F#代码转换为OCaml,对吗?

另外,我该如何在OCaml中实现F#的<<>>运算符?

(看起来|>运算符只需这样实现:let ( |> ) a b = b a ;;)


只是出于好奇,我花了很多时间将OCaml翻译成F#,因为OCaml中有大量的开源代码集,可以轻松地转换为F#,可以使用Visual Studio,并且有这里的支持。那么,从F#到OCaml,您获得了什么好处? - Guy Coder
1
@GuyCoder:首先,我不使用Windows。我在Linux上开发。我想我可以在Linux上使用Mono来开发F#,但我更熟悉OCaml及其工具链,我想我不想依赖于Mono运行时。我还喜欢OCaml具有的一些F#缺乏的功能,如函数器和一级模块。是的,我注意到有很多关于从OCaml转向F#的信息,但没有那么多关于反过来的。我们需要一些兼容性库/层。 - aneccodeal
你知道FSharp.PowerPack.Compatibility.dll吗?它并不能涵盖所有内容,但确实有所帮助。我正在翻译使用functors的ML代码,这是我第一次使用functors,我看到了它的吸引力,并且我真的喜欢OCaml的时间旅行调试特性。OCaml有很多优点,我会像推荐F#一样频繁地将其推荐给别人 :) - Guy Coder
此外,F# 可以从像 Camlp4 这样的预处理器中受益匪浅。根据我在论坛上看到的情况,我认为这不可能从微软内部实现。F# 有其他方法来实现一些这些功能,但您真的需要学习很多 F# 才能使用高级 Camlp4 进行翻译。 - Guy Coder
4个回答

16

为什么他们要定义并在F#中使用这个运算符,难道只是为了避免输入括号吗?

这是因为函数式编程的方式假设通过一系列的函数将值传递。比较以下两种写法:

let f1 str server =
    str
    |> parseUserName
    |> getUserByName server
    |> validateLogin <| DateTime.Now

let f2 str server =
    validateLogin(getUserByName(server, (parseUserName str)), DateTime.Now)
在第一个片段中,我们清楚地看到了值发生的所有事情。阅读第二个片段时,我们必须经过所有括号才能弄清楚正在发生的事情。
关于函数组合的这篇文章似乎与此相关。
所以,在普通生活中,大部分都是关于括号的。但是,管道运算符与部分函数应用和无点风格的编码密切相关。例如,参见Programming is "Pointless"
当管道|>和函数组合>><<运算符传递给高级函数时,它们可以产生另一种有趣的效果,例如在这里

“因为函数式编程的方式假定通过一系列函数来传递值。” 如果这是真的,那么OCaml在F#之前就已经有了这样的运算符。此外,您的示例不等同,因为您取消了柯里化并添加了多余的括号。validateLogin DateTime.Now (getUserByName server (parseUserName str)) - J D
6
请仔细阅读。我有意将“date”作为第二个参数,以展示“<|”运算符的有用性。 - Be Brave Be Like Ukraine

15

直接来自F#源码:

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)

12

OCaml Batteries支持这些运算符,但由于优先级、结合性和其他语法怪癖(比如Camlp4),它使用不同的符号。最近才确定了要使用哪些特定符号,有一些变化。请参见:Batteries API

val (|>) : 'a -> ('a -> 'b) -> 'b

函数应用。x |> f 等同于 f x。

val ( **> ) : ('a -> 'b) -> 'a -> 'b

函数应用。f **> x 相当于 f x。 注意:该运算符的名称并不是固定的,很快就会更改。

val (|-) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

函数复合。f |- g 是一个函数 x -> g (f x)。这也等同于应用 <** 两次。

val (-|) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

函数组合。f -| g 表示 fun x -> 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 |> f] 等同于 [f x]。

val ( %> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

管道函数组合。[f %> g] 是 [fun x -> g (f x)]。


1
这是很好知道的,那么' <| '会对camlp4造成问题吗? - aneccodeal
不,我认为只有在使用引号时才会出现 <<>> - lukstafi

5
我想知道为什么他们要在F#中定义和使用这个运算符,难道只是为了避免像这样输入括号吗?
非常好的问题。你提到的特定运算符(<|)在我看来几乎没什么用处。它让你在极少数情况下避免使用括号,但更普遍的情况是它通过引入更多的运算符使语法更加复杂,这使得那些经验较少的F#程序员(现在有很多人)更难理解你的代码。因此,我已经停止使用它。 |> 运算符则更有用,但仅因为它在 OCaml 无法处理的情况下帮助 F# 正确地推断类型。例如,下面是一些 OCaml 代码:
List.map (fun o -> o#foo) os

直接的等价物在F#中失败了,因为无法在读取其foo属性之前推断出 o 的类型,因此惯用的解决方法是重写代码,使用 |> 使F#能够在使用 foo 之前推断出 o 的类型:
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#程序员维护的生产代码非常有价值。


1
-1:你正在回答尚未提出的问题。这不是关于个人偏好或我们是否发现管道和组合运算符有用。此外,这也不是关于像您在审查我的答案时所做的那样用一系列“let”指令替换“|>”。然而,你提到的类型推断似乎很重要,所以如果你考虑清理你的答案,它会看起来更有建设性。 - Be Brave Be Like Ukraine
5
请保留回答中的额外信息。很多时候我提出问题是因为我需要了解某些东西,而我所问的并不是我所需要的,因为我不知道我确切需要什么。如果我知道,我就不会问那么多问题了。使用类型推断符号 |> 的好处我花了一些时间才明白,John 在这里免费提供了相关注释。读者可以选择忽略它。这并不是反对 Bytebuster :),而是说我仍然有关于 SO(Stack Overflow)的伦理问题。它是一个发鱼还是教人钓鱼的网站? - Guy Coder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接