Built in f# operator to compose functions with the same input but different outputs?

心不动则不痛 提交于 2020-01-04 11:09:29

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!