2

我了解<< compose 运算符采用两个函数,它们都接受并返回相同的类型。例如 (lhs:'a -> 'a) -> (rhs:'a -> 'a) -> 'a

我经常发现自己想要一些东西,比如(lhs:'a -> 'b) -> (rhs:'c -> 'b) -> 'b我对副作用感兴趣,而不是返回值 'b 可能是单位类型。仅当我连续两行将某些内容持久化到数据库时。

是否有内置函数或惯用的 F# 方式来执行此操作而无需编写类似的内容

let myCompose lhs rhs arg =
    lhs arg
    rhs arg
4

3 回答 3

5

后向合成算子(<<)定义为:

( << ) : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c`

应用两个谓词,它实际上是一个函数,它获取初始值,'a返回值'c,而值在'b内部处理。

从您提供的代码示例中,让我假设您需要将参数应用于两个谓词。做这件事有很多种方法:

丢弃(第一个)谓词返回的值,而是返回原始参数。WebSharper 中存在这样的运算符:

let ( |>! ) x f = f x; x
// Usage:
let ret =
    x
    |>! f1
    |>! f2
    |> f3

我喜欢这种方法,因为:

  • 它不会使事情复杂化;每个函数应用都是原子的,代码看起来更易读;
  • 它允许在三个或更多谓词中进行链接,就像上面的例子一样;

在这种情况下,f必须返回unit,但您可以轻松解决此问题:

let ( |>!! ) x f = ignore(f x); x

将参数应用于两个谓词,返回结果元组,与您自己的示例完全相同。有这样的算子OCaml,很容易适应F#:

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

正如@JackP 所注意到的,&&&已经在 F# 中为其他目的定义了,所以让我们使用另一个名称:

/// Applying two functions to the same argument.
let (.&.) f g x = (f x, g x)

// Usage
let ret1, ret2 =
   x
   |> (f .&. g)

注意上面的示例是针对函数应用的直接顺序。如果您需要以相反的顺序应用它们,则需要相应地修改代码。

于 2013-06-14T14:35:13.447 回答
4

反向反向组合运算符 (<<)不接受两个函数,它们都接受并返回相同的类型;唯一的约束是要应用的第一个函数的输出类型必须与它所组成的函数的输入类型相同。根据 MSDN,函数签名是:

// Signature:
( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3

// Usage:
func2 << func1

我不知道有一个内置的组合运算符可以按您的意愿工作,但是如果您在代码中经常使用这种模式并且拥有这样的运算符会简化您的代码,我认为定义您自己的模式是合理的。例如:

> let (<<!) func2 func1 arg = func1 arg; func2 arg;;

val ( <<! ) : func2:('a -> 'b) -> func1:('a -> unit) -> arg:'a -> 'b

或者,如果您知道这两个函数都将返回unit,则可以这样编写以将输出类型限制为unit

> let (<<!) func2 func1 arg = func1 arg; func2 arg; ();;

val ( <<! ) : func2:('a -> unit) -> func1:('a -> unit) -> arg:'a -> unit
于 2013-06-14T14:18:49.170 回答
3

要以任何所需的顺序组合任意数量的类型函数,您可以简单地折叠它们的列表:f:'a->unit

("whatever",[ printfn "funX: %A"; printfn "funY: %A"; printfn "funZ: %A" ])
||> List.fold (fun arg f -> f arg; arg )
|> ignore

进入 FSI

funX: "whatever"
funY: "whatever"
funZ: "whatever"
val it : unit = ()
于 2013-06-14T14:34:59.987 回答