DEV Community

Jakob Durstberger
Jakob Durstberger

Posted on

Partial Application and Parameter Ordering

Introduction

Something that often is given little consideration is the order of function parameters. Admittedly, frequently it doesn't make that a whole lot of a difference, but sometimes, specific parameter ordering can make our life easier.

⚠️This blog uses Clojure for its examples. I think Clojure's terseness is helpful in making good examples. If you don't know Clojure at all, check out my Very Short Introduction to Clojure.⚠️

Let's consider a function that adds value add tax (VAT) to a priced item. In a lot of countries, VAT differs between different types of products. We can easily encode this information in a map.

(def vat-percentages
  {:food-item 10
   :culture   13
   :default   20})
Enter fullscreen mode Exit fullscreen mode

Our function add-vat needs two arguments, vat-percentages and a product that needs to be priced. Therefore there are two ways to order the function parameters.

(defn add-vat
  [product vat-percentages]
  ...)

(defn add-vat
  [vat-percentages product]
  ...)
Enter fullscreen mode Exit fullscreen mode

At first, both options seem viable, but things get a bit more interesting when we use map to price a collection of products.

A primitive implementation that uses an anonymous function, would make the two options still seem pretty equal.

(map (fn [product] (add-vat product vat-percentages))
     products)

(map (fn [product] (add-vat vat-percentages product))
     products)
Enter fullscreen mode Exit fullscreen mode

But this approach doesn't leverage the whole toolkit of functional programming.

Partial Application

An underused tool is partial application.
Partial application allows us to take a function with arguments and pre-fill some of those arguments so we end up with a function that requires less arguments.

This can be demonstrated nicely with the + function.

;normal usage
(+ 2 3) ;=> 5

;partial application
(def add-3 (partial + 3))

(add-3 2) ;=>5
(add-3 9) ;=>12
Enter fullscreen mode Exit fullscreen mode

partial returns a new function which has 3 'baked in' as one of the arguments. We then assign the returned function to a new symbol add-3 as to be able to easily refer to it.

Looking back at the previous implementation of our map invocation we see now that we could try to leverage partial to pre-fill the tax information on our add-vat function.

The only consideration with partial is that arguments are "pre-filled" left-to-right.
This means that if we want to use partial on our add-vat function we have to put the vat-precentages before product.

Let's try it on our VAT example.

(def vat-percentages
  {:food-item 10
   :culture   13
   :default   20})

(defn add-vat
  [vat-percentages product]
  ...)

(defn apply-vat 
  [vat-percentages products]
    (map (partial add-vat vat-percentages) products))
Enter fullscreen mode Exit fullscreen mode

Compare this to the previous example.

(defn apply-vat 
  [vat-percentages products]
    (map (fn [product] (add-vat vat-percentages product)  products))
Enter fullscreen mode Exit fullscreen mode

It might not make a huge difference but it declutters our code and leaves only what's important.
This is just a short example but it shows that parameter ordering makes a difference and enables us to use more tools from our toolkit.

 Creating a Heuristic

From experience I have found that ordering parameters from more to less stable works quite well.
What I mean with stable is how likely it is that the value of the argument changes between invocations.

In the above example vat-precentages changes less often than the product that we are pricing.

Another example might help clarifying this way or thinking.
Let's consider a function that makes a network call.
The function requires a logger, a base-url of the api, a token, and then a payload to post.

Using the above technique most likely leads us towards something like this.

(defn post-to-api
  [logger base-url token payload]
  ...)
Enter fullscreen mode Exit fullscreen mode

When the system starts we could use partial to pre-fill logger and base-url. Once a user has authenticated we could use partial again to pre-fill token.

We are then left with a function that only requires a payload and puts little burden on the callee.

Conclusion

Partial application is a powerful tool to make your code more terse and can also be used to abstract required arguments from code further down.
To fully leverage partial application we should order function parameters from most to least stable.

Maybe you give it a go and let me know what you think?

Oldest comments (0)