2

I came across the need for a function with the signature 'a -> 'b -> ('a -> 'b -> 'c) -> 'c to use for applying two arguments when piping:

let apply2 x y f =
  f x y

I needed this because I am using a function

myFun : MyType -> TypeA -> TypeB -> ResultType

and I use it in another function like this:

let useCase someValue (valueA: TypeA) (valueB: TypeB) =
  someValue
  |> ...
  |> toMyType
  |> myFun
  |> apply2 valueA valueB

apply2 fits the bill, but I can't shake the feeling that I could use a built-in function or operator or that I am missing some more fundamental way of doing this (barring lambdas, which IMHO reads worse in this case). Note that I can't easily switch the parameter order of myFun (it's a Giraffe HttpHandler, so the last two parameters have to be HttpFunc and HttpContext, designated by TypeA and TypeB above).

Is the apply2 function with the signature I've described a fair thing to use in functional programming, or am I missing something obvious? If this is a well-known concept, does it have a better name?

cmeeren
  • 3,890
  • 2
  • 20
  • 50

3 Answers3

5

In my opinion the code is much clearer if you bind the intermediate value with let.

let useCase someValue (valueA: TypeA) (valueB: TypeB) =
    let myValue =
        someValue
        |> ...
        |> toMyType
    myFun myValue valueA valueB

You can also use backward pipes as follows

let useCase someValue (valueA: TypeA) (valueB: TypeB) =
    someValue
    |> ...
    |> toMyType
    |> myFun <| valueA <| valueB
hvester
  • 1,608
  • 12
  • 14
  • Accepted for suggesting that it might be more readable to just use a simple intermediate value (in addition to suggesting a way to pipe it). As for the actual piping syntax, the other answers also have good suggestions. – cmeeren May 03 '18 at 08:37
  • 1
    Note that using the backwards pipe operator like that can be confusing as it becomes difficult to discern what is actually happening in that last line. Don Syme (the creator of F#) advises caution when using that operator and *never* using it in the same line of code with the forward pipe for that reason. – TeaDrivenDev May 03 '18 at 23:16
  • @TeaDrivenDev I definitely wouldn't recommend the second solution. I added it mostly because I think it is still clearer than the solutions in other answer that use either (||>) or <|| – hvester May 04 '18 at 08:38
3

In your piping, you can replace:

 |> apply2 valueA valueB

with

 |> (||>) (valueA, valueA)

So ||> is the same as your apply2 function but with tupled arguments.

Gus
  • 25,839
  • 2
  • 51
  • 76
2

You could use a little trick here:

let useCase someValue ((valueA, valueB) as tuple) =
    someValue
    |>  ...
    |>  toMyType
    |>  myFun
    <|| tuple

or just

let useCase someValue tuple =
    ...
Szer
  • 3,426
  • 3
  • 16
  • 36