是否存在某种形式的内置/术语我不知道那种有点但不同的“组合”两个'a -> unit
函数来产生单个函数; e.g:
let project event =
event |> logDirections
event |> stashDirections
let dispatch (batch:EncodedEventBatch) =
batch.chooseOfUnion () |> Seq.iter project
可能会成为:
let project = logDirections FOLLOWEDBY stashDirections
let dispatch (batch:EncodedEventBatch) =
batch.chooseOfUnion () |> Seq.iter project
然后:
let dispatch (batch:EncodedEventBatch) =
batch.chooseOfUnion () |> Seq.iter (logDirections FOLLOWEDBY stashDirections)
我想有人可能会将其与tee
进行比较(如FSFFAP's Railway Oriented Programming series中提到的那样)。
(它需要将相同的arg传递给两者并且我正在寻求按顺序运行它们而没有任何异常处理技巧问题等。)
(我知道我可以做let project fs arg = fs |> Seq.iter (fun f -> f arg)
,但我想知道是否有内置的内容和/或某种形式的组合库我不知道)
答案 0 :(得分:5)
Klark的apply
函数是解决问题最直接的方法。
如果你想深入挖掘并更普遍地理解这个概念,那么你可以说你解除顺序组合操作,从处理值到< EM>功能
首先,F#中的;
构造可以被视为顺序合成运算符。可悲的是,你不能完全使用它,例如(;)
(因为它在第二个参数中是特殊和懒惰的)但我们可以定义我们自己的运算符来探索这个想法:
let ($) a b = a; b
因此,printfn "hi" $ 1
现在是副作用操作的顺序组合,而某些表达式的计算结果为1
,它与printfn "hi"; 1
的作用相同。
下一步是定义一个提升操作,将操作值的二元运算符转换为处理函数的二元运算符:
let lift op g h = (fun a -> op (g a) (h a))
而不是写例如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
seq2
和seq3
函数只构成两个操作,而seqN
与Klark的apply
函数完全相同。
应该说我写这个答案并不是因为我认为用这种方式实现中的东西是有用的,但正如你提到的铁路导向编程并要求更深层次的概念背后这一点很有趣,看看在函数式语言中如何编写。
答案 1 :(得分:3)
您可以将一组函数应用于给定数据吗? 例如。你可以定义:
let apply (arg:'a) (fs:(('a->unit) seq)) = fs |> Seq.iter (fun f -> f arg)
然后你就能做到这样的事情:
apply 1 [(fun x -> printfn "%d" (x + 1)); (fun y -> printfn "%d" (y + 2))]