10

我正在将一些 F# 代码转换为 OCaml,并且我看到了这个管道运算符的很多用途<|,例如:

let printPeg expr =
    printfn "%s" <| pegToString expr

<|运算符显然被定义为:

# let ( <| ) a b = a b ;;
val ( <| ) : ('a -> 'b) -> 'a -> 'b = <fun>

我想知道为什么他们费心在 F# 中定义和使用这个运算符,是不是为了避免像这样放入括号?:

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

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

另外,我将如何在 Ocaml 中实现 F#<<和运算符?>>

|>运营商似乎只是let ( |> ) a b = b a ;;:)

4

4 回答 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"

管道|>和函数组合>> <<运算符在传递给更高级别的函数时会产生另一个有趣的效果,比如这里

于 2013-01-19T06:03:33.897 回答
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)
于 2013-01-19T04:15:30.787 回答
12

OCaml Batteries 支持这些运算符,但出于优先级、关联性和其他语法怪癖(如 Camlp4)的原因,它使用不同的符号。最近刚刚确定了使用哪些特定符号,有一些变化。请参阅:电池 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 (fx)。这也相当于应用 <** 两次。

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

功能组成。f -| g 很有趣 x -> f (gx)。在数学上,这是算子 o。

电池后备箱提供:

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

功能应用。[f @@ x] 等价于 [fx]。

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

函数组成:数学[o]运算符。

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

“管道”:功能应用。[x |> f] 等价于 [fx]。

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

管道功能组成。[f %> g] 是 [fun x -> g (fx)]。

于 2013-01-19T12:21:47.280 回答
5

我想知道他们为什么要费心在 F# 中定义和使用这个运算符,只是为了避免像这样放入括号吗?

很好的问题。您所指的特定运算符 ( <|) 是非常无用的 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)

我很少使用其他运算符 ( <<and >>),因为它们也使语法复杂化。我也不喜欢引入大量运算符的解析器组合库。

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

于 2013-01-20T12:06:08.430 回答