Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a step-by-step process for converting a series of pipelines into a functional composition?

Is there a step-by-step process for converting several pipelines into a functional composition?

Side note: Did I even use the term "functional composition" in the right context?

I have the following code:

let purchase qty inventory =
    buyFrom inventory qty |> fromInventory
                          |> reduceBy qty

I wanted to refactor this code to support functional composition.

So I thought I could do this:

let purchase qty inventory =
    buyFrom inventory >> fromInventory >> reduceBy qty

This code compiles. However, it appears that I am missing an argument when attempting to invoke the purchase function within the debugger.

Here's my test for the function:

[<Test>]
let ``buy last product``() =
    [SomeProduct] |> purchase 1
                  |> should equal []

NOTE:

I'm still struggling with FP fundamentals and still do not understand the process of converting a pipeline work flow into a functional composition.

I think I understand that functional composition relies on partial application which I think I have a handle on now. Thus, I have reason to believe that my refactored function is missing an argument.

Any guidance on a workflow I can use to get better at functional composition?

Appendix:

type Product =
    | SomeProduct

type TransactionResult = { Price:decimal; Inventory:Product list }

(* Functions *)

let priceOf qty product =
    match product with
    | SomeProduct -> match qty with
                     | 3 -> decimal (qty - 1) * 1.99m
                     | _ -> decimal (qty) * 1.99m

let rec reduceBy count list =
    match list with
    | first::remaining when count = 1         -> remaining
    | first::second::remaining when count > 1 -> second::remaining |> reduceBy (count - 1)
    | remaining when count = 0                -> remaining
    | _ -> []

let buyFrom inventory qty =
    { Price=priceOf qty (List.head inventory); Inventory=inventory }

let fromInventory transactionResult =
    transactionResult.Inventory
like image 997
Scott Nimrod Avatar asked Jul 21 '16 12:07

Scott Nimrod


People also ask

What is pipeline in functional programming?

Collection pipelines are a programming pattern where you organize some computation as a sequence of operations which compose by taking a collection as output of one operation and feeding it into the next. (Common operations are filter, map, and reduce.)

What is pipeline transformation?

The transformation pipeline takes the data read from source data based on the Source Attributes setting and transforms it to a necessary target format with one or multiple operators.

Can pipeline have multiple estimators?

Pipeline can be used to chain multiple estimators into one. This is useful as there is often a fixed sequence of steps in processing the data, for example feature selection, normalization and classification.

What is a transducer programming?

Let's start with a definition: A transducer is a function that takes a reducer function and returns a reducer function. A reducer is a binary function that takes an accumulator and a value and returns an accumulator.


2 Answers

The direct translation to a composed form would be either:

Using "forward" function composition:

let purchase qty inventory = (buyFrom inventory >> fromInventory >> reduceBy qty) qty

Using "backward" function composition (Personally, I'm not sure I like the term "backward" because this is function composition in the traditional mathemetical sense of the term, i.e. f << g = f ∘ g):

let purchase' qty inventory = reduceBy qty << fromInventory << buyFrom inventory <| qty

As you can perhaps see from the above examples, it's really most valuable when it lets you skip the last argument entirely. In this example, you can't do that because you depend on qty twice so I'd recommend you stick with piping here.


In general, if you have a function f(x) and a function g(x) you can write a composed function h(x) = (f ∘ g)(x) in the following ways:

let h x = f (g (x))
let h x = x |> g |> f
let h x = f << g <| x
let h x = (g >> f) x

In the second two cases, using function composition, you can omit the x entirely.

let h = f << g
let h = g >> f
like image 94
TheInnerLight Avatar answered Sep 24 '22 18:09

TheInnerLight


Usually, if you can massage your pipeline expression to have the form

let f x y value = value |> f1 x |> f2 y |> f3

you can refactor it to

let f x y = f1 x >> f2 y >> f3

That's not quite the case here, as your expression begins with a function call (buyFrom inventory qty), the value of which is then piped to fromInventory.

In this case, I think you can refactor to

let purchase' inventory qty =
    qty |> buyFrom inventory |> fromInventory |> reduceBy qty

but then you can't really get any further because qty is also needed as an argument to reduceBy.

I think you might be able to eliminate both occurrences of qty by using the Reader or State monad, but it's hardly worth the effort.

like image 30
Mark Seemann Avatar answered Sep 24 '22 18:09

Mark Seemann