用相同的arg类型组合2(或n)('a - >单位)函数

Rub*_*ink 3 f# action side-effects composition function-composition

是否有某种形式的内置/术语我不知道它有点但不同的"组成"两个'a -> unit功能产生一个单一的功能; 例如:

let project event =
    event |> logDirections
    event |> stashDirections
let dispatch (batch:EncodedEventBatch) =
    batch.chooseOfUnion () |> Seq.iter project
Run Code Online (Sandbox Code Playgroud)

可能成为:

let project = logDirections FOLLOWEDBY stashDirections
let dispatch (batch:EncodedEventBatch) =
    batch.chooseOfUnion () |> Seq.iter project
Run Code Online (Sandbox Code Playgroud)

然后:

let dispatch (batch:EncodedEventBatch) =
    batch.chooseOfUnion () |> Seq.iter (logDirections FOLLOWEDBY stashDirections)
Run Code Online (Sandbox Code Playgroud)

我想有人可能会比较它tee(如FSFFAP的铁路导向编程系列中提到的那样).

(它需要将相同的arg传递给两者并且我正在寻求按顺序运行它们而没有任何异常处理技巧问题等)

(我知道我可以做,let project fs arg = fs |> Seq.iter (fun f -> f arg)但我想知道是否有内置的和/或某种形式的组合库我不知道)

Tom*_*cek 5

applyKlark 的功能是解决问题最直接的方法.

如果你想更深入地挖掘并更普遍地理解这个概念,那么你可以说你正在提升顺序组合操作,从处理到处理函数.

首先,;F#中的构造可以被视为顺序合成运算符.可悲的是,你不能完全使用它,例如(;)(因为它在第二个参数中是特殊和懒惰的)但我们可以定义我们自己的运算符来探索这个想法:

let ($) a b = a; b
Run Code Online (Sandbox Code Playgroud)

所以,printfn "hi" $ 1现在是一个副作用操作的顺序组合和一些评估的表达式,1并且它做同样的事情printfn "hi"; 1.

下一步是定义一个提升操作,将操作值的二元运算符转换为处理函数的二元运算符:

let lift op g h = (fun a -> op (g a) (h a))
Run Code Online (Sandbox Code Playgroud)

fun x -> foo x + bar x你可以写,而不是写作例如lift (+) foo bar.所以你有一个无点的方式来编写相同的东西 - 只使用适用于函数的操作.

现在,您可以使用lift函数和顺序组合运算符实现所需的功能:

let seq2 a b = lift ($) a b
let seq3 a b c = lift ($) (lift ($) a b) c
let seqN l = Seq.reduce (lift ($)) l
Run Code Online (Sandbox Code Playgroud)

seq2seq3功能组合刚好在两个操作,而seqN做同样的事情的Klark的apply功能.

应该说我写这个答案并不是因为我觉得以这种方式在F#中实现它是有用的,但正如你提到的铁路导向编程并要求更深层次的概念背后,有趣的是看事情如何用函数式语言编写.