2013-01-19 139 views
7

我把一些F#代码ocaml的,我看到很多这样的管道运营商<|的用途,例如:转换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 ;;

+0

出于好奇,我花了很多时间从OCaml转换到F#,因为OCaml中有大量的开放代码,易于翻译为F#,能够使用Visual Studio以及在这里支持,有什么好处你从F#转到OCaml会获益吗? –

+0

@GuyCoder:首先,我不在Windows上。我在Linux上开发。我想我可以使用Mono在Linux上开发F#,但我更熟悉OCaml及其工具链,我想我不想依赖Mono运行时。我也喜欢OCaml的一些功能,F#缺少仿函数和一流的模块。是的,我注意到有很多关于从OCaml到F#的信息,但没有太多其他方面的信息。我们需要某种兼容性库/图层。 – aneccodeal

+0

您是否知道[FSharp.PowerPack.Compatibility.dll](https://github.com/fsharp/powerpack/tree/master/src/FSharp.PowerPack.Compatibility)?它并不包含所有内容,但它确实有帮助。我目前正在使用函数来翻译ML代码,这是我第一次使用函子来看待它的吸引力,而且我真的很喜欢[时间旅行](http://caml.inria.fr/pub/docs/manual-ocaml- 4.00/manual030.html)调试OCaml的功能。 OCaml有很多提供,我会推荐它像F#一样频繁。 :) –

回答

11

直接从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) 
14

为什么他们懒得去定义和使用该运营商在F#中,它只是让他们可以避免投放父母?

这是因为编程的功能方式假设通过一系列函数对值进行线程化。比较:

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

let f2 str server = 
    validateLogin(getUserByName(server, (parseUserName str)), DateTime.Now) 

在第一个片段中,我们清楚地看到了与该值发生的所有事情。读第二篇,我们必须通过所有的parens来弄清楚发生了什么。

This article关于功能组成似乎是相关的。

所以是的,在一个正常的生活中,它主要是关于parens。而且,管线运营商与部分功能应用和无点式编码密切相关。例如,请参阅Programming is "Pointless"。当它们被传递到更高级别的功能,如here

管道|>和功能组成>><<运营商可以产生另一个有趣的效果。

+0

-1“因为编程的功能方式假定通过一系列函数对值进行线程化”。如果这是真的,那么OCaml会在F#之前拥有这样的运营商。此外,你的例子并不相同,因为你没有烦恼,并添加了多余的括号。 'validateLogin DateTime.Now(getUserByName server(parseUserName str))''。 –

+5

@JonHarrop请仔细阅读。我故意做了'date' **秒**参数来证明'<|'运算符的有用性。 – bytebuster

9

OCaml电池支持这些操作符,但由于优先级,关联性和其他语法怪癖(如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是有趣的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)]。

+0

+1注意到Battries。 –

+0

这很好理解,'<|'导致camlp4问题? – aneccodeal

+0

不,我认为只有'<<' and '>>'做,如果你使用报价。 – lukstafi

6

我想知道他们为什么打扰在F#中定义和使用这个操作符,是不是因为他们可以避免把这样的东西放进去?

优秀的问题。你提到的具体操作符(<|)是相当无用的IME。它可以避免在极少数情况下使用圆括号,但更普遍的是,通过拖入更多的操作符会使语法复杂化,并且使经验较少的F#程序员(现在有很多人)了解您的代码变得更加困难。所以我已经停止使用它了。

|>运算符更有用,但仅仅是因为它有助于F#在OCaml没有问题的情况下正确推断类型。例如,这里是一些OCaml的:

List.map (fun o -> o#foo) os 

直接等同于F#失败,因为o类型无法读取其foo财产之前,所以惯用的解决方案是使用|>所以要改写这样的代码推断F#可以推断ofoo前的类型用于:

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'指令代替'|>',就像你回顾我的答案一样。然而,关于类型推断的观点似乎很重要,所以如果考虑清理答案,那看起来会更有建设性。 – bytebuster

+5

+1请继续在答案中提供额外信息。有很多次我问一个问题,因为我需要知道一些事情,而我所要求的不是我所需要的,因为我不知道我需要什么。如果我做了,我不会问许多问题。带类型推断的|>的用途花了我一些时间去理解,John在这里免费注释它。读者可以自由地忽略它。这不是一个反对比喻的东西:)但要说我仍然有问题的SO的气质。看到放鱼或教人钓鱼吗? –