Coffee and Contemplation A developer blog

Higher order functions and where to find them - Decorators

We saw what higher order functions are and how they can be used in creating partially applied functions in previous posts. Now let us see how we can use higher order functions to implement python decorators.

Let us say you want to intercept your function call to log the arguments passed and the return value. ie.

def add(a, b):
    return a + b

add(2, 3)
# When called with 2 and 3 as arguments, it should print
> Function add called with arguments: 2, 3
> Function add returned: 5

It is quite easy to do this. Let us start with defining a function log.

def log(fun):
    def inner(*args, **kwargs):
        print(
            f"Function {fun.__name__} "
            f"called with arguments: {*args, *kwargs}")
        ret_value = fun(*args, **kwargs)
        print(f"Function {fun.__name__} returned: {ret_value}")
        return ret_value
    return inner

add_with_logging = log(add)
add_with_logging(1, 2)

# Prints the following
> Function add called with arguments: (1, 2)
> Function add returned: 3

But this isn’t good. You are creating a new function and have to replace all usages of add with add_with_logging. There is an easy fix.

# Redefine the name add to log(add)
add = log(add)
add(1, 2)
# Prints the following
> Function add called with arguments: (1, 2)
> Function add returned: 3

This is called a decorator pattern and is quite widely used in python. Python provides some syntactic sugar for this usage. All you need to do is add @log before the function definition.

@log
def add(a, b):
    return a + b
add(1, 2)
# Prints the following
> Function add called with arguments: (1, 2)
> Function add returned: 3

Higher order functions and where to find them - Partial Application

In the previous post, we discussed higher order functions. Ones that take functions as arguments and ones that return functions as arguments. Now let us look at some that do both.

In languages like haskell, there is a feature called Currying. When a function which takes multiple arguments is called with less than the number of expected arguments, it returns another function with the arguments “partially applied”1. For Eg:-

-- Define a function
add a b = a + b
-- Call the function.
-- There are no braces for function calls in haskell
add 1 2  -- Returns 3
-- Below even though add is a 2 argument function,
-- we can pass a single argument and "partially apply" the argument
addTen = add 10
-- addTen is a function
addTen 32   -- Returns 42

Now, let us try and get this behavior in python. Mind you, we can’t get so nice a syntax with our python implementation. What can we expect?

# define our add function
def add(a, b):
    return a + b

To partially apply a single argument, let us define a function that can be used as follows:

addTen = partial(add, 10)

partial should be a function that returns a closure which has access to the function and the argument passed to it.

def partial(fun, arg1):
    def inner(*args):
        return fun(arg1, *args)
    return inner

addTen = partial(add, 10)
addTen(32)    # Returns 42

A better production ready version for partial is available in the functools library of python. You can access it by from functools import partial

Partial can be applied to functions which has more than 2 arguments as well.

def add3(a, b, c):
    return a + b + c
addTen = partial(partial(add3, 4), 6)
addTen(32)  # Returns 42

  1. There are subtle differences between currying and partial application. They aren’t significant for this article. 

Higher order functions and where to find them

My first encounter with a higher order function was when I ran man qsort and noticed its type.

void qsort(void *base, size_t nmemb, size_t size,
            int (*compar)(const void *, const void *));

For someone who had only seen primitive types like int and long and char, int (*compar)(const void *, const void *) was quite the surprise. I later found out that it was the type for a function which took two arguments and returned an int. Complicated looking syntax aside, this is quite interesting. Passing a function allows you to create a generic qsort function which can be use to sort anything with a custom sorting logic.

A higher order function is a function that takes other functions as arguments or return functions.

When I picked up python a few years later, higher order functions were everywhere. And pretty easy to use.

# Let us define a function that prints "Hello world"
def hello():
    print("Hello world")

# call is a higher order function which just calls a function passed to it without arguments
def call(fun):
    fun()

# Look ma! I passed a function
call(hello)  # prints Hello world

Now, what if you want to make a function which can call functions which takes arguments. This is were python’s *args and **kwargs come into picture. Let us redefine our call function.

# add takes two numbers and return the sum
def add(a, b):
    return a+b

def call(fun, *args, **kwargs):
    return fun(*args, **kwargs)

# Call add with positional arguments
call(add, 1, 2)  # returns 3

# Call add with named arguments
call(add, a=1, b=2)  # returns 3

Functions in python can return other functions too

def make_hello():
    def say_hello():
        print("Hello world")
    return say_hello

# Call make_hello and assign the return function to a variable.
# Then use it like any normal function
hello = make_hello()
hello()

Now, let us do something fun with this

def make_adder(n):
    def adder(m):
        return m + n
    return adder

add_one = make_adder(1)
add_one(10) # returns 11

add_42 = make_adder(42)
add_42(10) # returns 52

Here, we return a function adder from inside make_adder. Notice how the inner function has access to the variable n even after the execution of make_adder is complete. This inner function along with its scope is called a closure.

Languages like python, javascript, dart etc supports closures.

How to quit vim

Vim has multiple modes. You normally insert text in the Insert mode. Normal mode is where you move around and give commands to vim.

Esc will take you to normal mode(usually). You can enter a command starting with a :. Quit vim with the below command:

<Esc>:qa!
  • q - quit
  • a - all
  • ! - force do the operation. This will quit all the buffers including the unsaved ones.