Clojure: Functional

It's been awhile, but finally I've found some time to play with Clojure again.

Now on for some more functional programming techniques: partials, closures, higher order functions, and so on.

Recap: composite types as functions

Composite types can be used as functions of their elements, I wrote about this previously. A small recap:

user> (def colormap {:red 255 :green 33 :blue 0})
#'user/colormap
user> (colormap :green)
33
user> (map colormap #{:red :green})
(255 33)

Minor syntactic sugar you might say, but concise and still clear.

Composition

The comp function takes some functions and creates a function that is the composition of all those functions, ie.: comp(f,g,h)(x) ~ f(g(h(x))). That is, comp generates a function that pipes its arguments through the functions passed into comp.

Example: count elements for which a predicate is true. Construct a function countif which applies the filter and count functions, and call it with a func literal that checks whether the arg is greater than 1:

user> (def countif (comp count filter))
#'user/countif
user> (countif #(< 1 %) [-1 0 1 2 3])
2

Partial application

The partial function takes a function, fills in some values and creates a new function (not entirely unlike currying). Args supplied to partial are filled in left-to-right.

Construct a function that adds 5 to its arg and call it:

user> ((partial + 5) 2)
7

It's possible to fill in all needed args to create a no-arg function. Eg., create a function that will return a random integer up to 10:

user> ((partial rand-int 10))
8

I wonder if using some form of function literal wouldn't be more versatile (and still concise). Eg. it's cumbersome to create partials from functions that don't have their args in the order needed. (I have to admit though that using function literals gives me a certain Perlish feeling -- tiny changes in the source that lead to huge changes in semantics).

Another example -- trim and lowercase a string, split and convert to keywords:

user> (use '[clojure.string :only (join split lower-case trim)])
nil
user> (def tokens (comp
        (partial map keyword)
        #(split % #"[:,; ]+")
        lower-case
        trim))
#'user/tokens
user> (tokens "FOO, Bar  quux ; qua   ")
(:foo :bar :quux :qua)

Note that with two functions above the interface of functions does not match for a simple pipeline. Firstly, the keyword function takes only one arg, not a list -- constructing a partial with map takes care of that. Secondly, the split function needs to know on what to split. A partial doesn't work here since the partial function applies arguments left-to-right. The function literal wraps the split function with the desired regexp.

Complement

This is a not-function generator, ie. it wraps a function in a function logically negating the output of the previous function.

user> ((complement number?) 1)
false
user> ((complement number?) :a)
true

Functions as data

There were quite some examples of treating functions as first-class objects. The book gives one more -- attaching bits of test code to functions. The built-in unit testing capability uses test functions attached to a functions' metadata like this:

user> (defn f {:test (fn [] (assert (= (f 1) 4)))}  [v] (+ v 3))
#'user/f
user> (f 1)
4
user> (use '[clojure.test :as t])
nil
user> (t/run-tests)

Testing user

Ran 1 tests containing 0 assertions.
0 failures, 0 errors.
{:type :summary, :pass 0, :test 1, :error 0, :fail 0}

An error in function or test, btw., produces a rather fearsome and not too informative trace -- IMHO there's room for improvement here.

Higher order functions

There were already some of those in the text covered so far. Another worthwhile example is the sort-by function, which sorts a sequence and takes a function to preprocess the data to be sorted from each element. Some examples using the regular sort function:

user> (sort [2 3 1])
(1 2 3)
user> (sort > [2 3 1])
(3 2 1)
user> (sort  ["a" 1 2])
ClassCastException ...

The sort-by function takes a function parameter used to preprocess each item. For instance, casting, extracting from a collection, or a calculation:

user> (sort-by str ["a" 1 2])
(1 2 "a")
user> (sort-by :a [{:a 33 :b 1 } {:a 5 :b 1} {:a 31 :b 3}])
({:a 5, :b 1} {:a 31, :b 3} {:a 33, :b 1})
user> (sort-by #(/ (:a %) (:b %)) [{:a 33 :b 1 } {:a 5 :b 1} {:a 31 :b 3}])
({:a 5, :b 1} {:a 31, :b 3} {:a 33, :b 1})

Functions that return functions can also be created outside of comp, partial et al. For instance, to parameterize a calculation for use with sort-by I could define a HoF such as:

user> (defn addcols [colnames] (fn [row] (apply + (map row colnames))))
#'user/addcols
user> ((addcols [:a :b]) {:a 1 :b 3})
4
user> (sort-by (addcols [:a :b]) [{:a 33 :b 1 } {:a 5 :b 1} {:a 31 :b 3}])
({:a 5, :b 1} {:a 33, :b 1} {:a 31, :b 3})

Note that the function-generating function is parameterized (the colnames), and the generated function also takes a parameter (the row). This is not unlike to how in Python a decorator (ie. the function generating the decorator) can be parameterized.

Pure functions

Pure functions are functions that:

  • Only rely on their inputs, ie. the output is fully determined by their input
  • Cause no observable side effects (for some value of "observable").
Pure functions have some nice properties: a) they (often) can be
easily optimized by caching / memoization techniques,
and they're b) easier to test than non-pure functions.

Named Args

Named arguments lets you pass arguments by name into functions, instead of having to rely on position. This is useful to make APIs more self-describing and easier to use, eg. when there are long arglists, especially when not all of them are used all of the time. In Python this could look something like this:

def create_widget(model="foobar", width=23):
    widget = _widgets(model)
    widget.width = width
    ...
    return widget

Clojure supports named arguments via its destructuring mechanisms and the "optional" flag &:

user> (defn create-widget [& {:keys [model width] :or {model     "foobar" width 23}}]
        (let [widget (widgets model)]
          (set-width widget width)))

Clojures' destructuring is pretty featureful. I am however unsure I like the application for named args. Pythons' syntax here feels cleaner and less cluttered. Also, if you try to use named args in a positional style (which you can do in Python) results in this:

user> (defn f [& {:keys [a b] :or {a 1 b 0}}] (* a b))
#'user/f
user> (f :a 2 :b 3)
6
user> (f 2 3)
0
user> (f 2 :b 3)
IllegalArgumentException ...

So, while the named args worked as expected in the first example, the parameters in the second example are ignored, because they're put into a map {2 3} (Python would throw an exception here, complaining about an unexpected keyword arg). And finally, Clojure will always try to construct a map with this destructuring and expect alternating key/value pairs, which is why the last example fails.

Constraints

It is possible to attach pre- and postcondition vectors to functions. Every entry is evaluated and must return a truthy value, otherwise the constraint fails.

For instance a function that enforces argument data type and matches against a regexp:

user> (defn dbcon [constr] {:pre     [(string? constr) (re-seq #"^postgresql://" constr)]}
        ; open connection...
        constr)
user> (dbcon 1)
AssertionError Assert failed: (string? constr)  user/dbcon     (NO_SOURCE_FILE:1)
user> (dbcon ["x"])
AssertionError Assert failed: (string? constr)  user/dbcon     (NO_SOURCE_FILE:1)
user> (dbcon "")
AssertionError Assert failed: (re-seq #"^postgresql://"     constr)  user/dbcon (NO_SOURCE_FILE:1)

By the way, preconditions with an empty function body don't seem to behave quite right:

user> ((fn [a] {:pre [(= 0 a)]}) 2)
{:pre [false]}

However, if I explicitly return something, the precondition works as expected:

user> ((fn [a] {:pre [(= 0 a)]} nil) 2)
AssertionError Assert failed: (= 0 a)      user/eval1651/fn--1652 (NO_SOURCE_FILE:1)

Closures

A closure is a function that has access to a local from the context it's defined in (it closes over said local).

user> (defn f [a]
        (fn [b] (+ a b)))
#'user/f
user> ((f 2) 3)
5

Of course this also works with function literals:

user> (defn larger-than [thresh seq]
        (filter #(< thresh %) seq))
#'user/larger-than
user> (larger-than 2 [0 1 2 3 4 5])
(3 4 5)

Poor Mans' Objects

By bundling up values and closures in a map, it's possible to encapsulate data and behaviour -- a Poor Mans' Object. Consider a "constructor" function for a "Employee class" which only has a wage attribute and a raise method:

user> (defn employee [wage]
        {:wage wage
         :raise #(employee (+ % wage))})
#'user/employee
user> (def johnny (employee 10))
#'user/johnny
user> (:wage johnny)
10
user> ((:raise johnny) 20)
{:wage 30, :raise #<user$employee$fn__779 user$employee$fn__779@5ee988c6>}

More vars and closures could be added to the "employee" map in the same vein.

Whew. Functions and functional decomposition is right at the heart of Clojure, and it's quite natural that Clojure provides a lot of idioms here. Next, something very lispish -- macros!

 · 
peter
 ·