问题
I understand the << compose operator takes two functions that both take in and return the same type. e.g. (lhs:'a -> 'a) -> (rhs:'a -> 'a) -> 'a
I often find myself wanting something like (lhs:'a -> 'b) -> (rhs:'c -> 'b) -> 'b
in cases where I'm interested in side affects and not the return value 'b is probably the unit type. This is only when I have two lines in succession where I'm persisting something to a database.
Is there a built in function or idiomatic F# way of doing this without writing something like
let myCompose lhs rhs arg =
lhs arg
rhs arg
回答1:
Backward composition operator (<<)
is defined as:
( << ) : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c`
With two predicates applied, it is actually a function that takes initial value of 'a
returning 'c
, while the value of 'b
is processed inside.
From the code sample you provided, let me assume that you need applying an argument to both predicates. There are several ways to do this:
Discarding the value returned by the (first) predicate, returning the original argument instead. Such operator exists in WebSharper:
let ( |>! ) x f = f x; x
// Usage:
let ret =
x
|>! f1
|>! f2
|> f3
I like this approach because:
- it does not complicate things; each function application is atomic, and the code appears more readable;
- it allows chaining throughout three or more predicates, like in the example above;
In this case, f
must return unit
, but you can easily work this around:
let ( |>!! ) x f = ignore(f x); x
Applying the argument to both predicates, returning a tuple of results, exactly as in your own example. There's such operator OCaml, easy to adapt to F#:
val (&&&) : ('a -> 'b) -> ('a -> 'c) -> 'a -> 'b * 'c
As @JackP noticed, &&&
is already defined in F# for another purpose, so let's use another name:
/// Applying two functions to the same argument.
let (.&.) f g x = (f x, g x)
// Usage
let ret1, ret2 =
x
|> (f .&. g)
Note The samples above are for straight order of function application. If you need them applied in a reverse order, you need to modify the code accordingly.
回答2:
The backward or reverse composition operator (<<)
does not take two functions that both take in and return the same type; the only constraint is that the output type of the first function to be applied must be the same as the input type of the function it's being composed into. According to MSDN, the function signature is:
// Signature:
( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
// Usage:
func2 << func1
I don't know of a built-in composition operator that works like you want, but if this pattern is something you use frequently in your code and having such an operator would simplify your code, I think it's reasonable to define your own. For example:
> let (<<!) func2 func1 arg = func1 arg; func2 arg;;
val ( <<! ) : func2:('a -> 'b) -> func1:('a -> unit) -> arg:'a -> 'b
Or, if you know both functions are going to return unit
, you can write it like this to constrain the output type to be unit
:
> let (<<!) func2 func1 arg = func1 arg; func2 arg; ();;
val ( <<! ) : func2:('a -> unit) -> func1:('a -> unit) -> arg:'a -> unit
回答3:
For composing of any number of functions of type f:'a->unit
in any desired order you may simply fold their list:
("whatever",[ printfn "funX: %A"; printfn "funY: %A"; printfn "funZ: %A" ])
||> List.fold (fun arg f -> f arg; arg )
|> ignore
getting in FSI
funX: "whatever"
funY: "whatever"
funZ: "whatever"
val it : unit = ()
来源:https://stackoverflow.com/questions/17109624/built-in-f-operator-to-compose-functions-with-the-same-input-but-different-outp