Daniel Compton

The personal blog of Daniel Compton - Projects

What is the Job To Be Done of a web framework?

The Clojure community has a knack for looking at things and distilling them to their essence. I think the recent discussion on whether Clojure needs a web framework is another opportunity for that.

I don’t think Clojure doesn’t need a web framework, because Clojure is a programming language and doesn’t need anything except a nice, warm JVM. Even if you reframe the question, “Do people need a Clojure web framework?”, I still don’t think its asking the right thing. People don’t need a web framework, they need what a web framework provides for them, and different people have very different needs.

If we use the Jobs To Be Done theory we can see what job different groups of people ‘hire’ a framework for:

“The Business”

The business hires a popular framework because

Recruiters

Recruiters like to be able to pick out a particular technology and match you to their clients requirements. Like it or not, thats the world that we live in. If there was a common framework that people used, this part would be easier for them and the developer trying to get hired.

The CTO

The CTO shares many of the same concerns with the business, she also

The junior developer

A popular framework is great for a junior developer, they hire it for

The senior developer

A framework is least useful to the senior developer, as they are often capable of combining libraries with the functionality they need to build their own webapp. A framework is still useful if it allows them to swap out libraries for their own choice. They may find themselves restricted by the framework if they want to step outside of the bounds that the framework provides.

If you agree with my (crude) characterisations then you can see that a framework provides many different things to many different people. Clojure doesn’t necessarily need a web framework, but in my opinion it does need to satisfy each of the parties involved with the development process if it wants to be used more commonly in web app development.

Meeting our users where they are

Several years ago fresh out of University, I needed to update some software at the company I was working at. The update required the users to make a change to their local machines configuration. I knew this would be a bit tricky for some of the users so I sent an email with an explanation and instructions. However instead of including the instructions in the email, I just gave a link to the manufacturers documentation. I thought about copying the five lines of instructions into the email but didn’t. I reasoned that “Of course they can just click the link and read it. There’s nothing hard about that”. In my naive idealised world full of people who enjoyed reading documentation, this would have been fine. In the real world, the change didn’t go very well.

After sending the email, many users didn’t click the link to the instructions and didn’t make any changes. This caused confusion and frustration for them and me over the next few days. After thinking about this on the weekend I knew I had to resend the email. I resent the explanation email but included the documentation inline and added an explanatory picture. This time, everyone picked up quickly what they needed to change and the switch went easily.

Including a link to manufacturer documentation seemed perfectly fine in theory. However it would have been better to let go of my expectations of the users and give them what they needed, rather than what I thought they should want. Often as technologists we have a tendency to build our software for how we want our users to be, not how they are. In certain cases that can work, but most of the time it is better to meet our users where they are.

Repeating yourself in Clojure

Clojure has a number of ways to repeat actions or sequences, but sometimes it can be a little confusing to know which to pick, or to remember all of the options. This post will discuss repeat, cycle, iterate, repeatedly, constantly, and dotimes. From the names, you can tell they all involve repetition, but beyond that it’s not obvious at first glance what they do. Lets dive in!

Repeat

Repeat seems like the first tool in the bag when you want to repeat something. The docstring says “Returns a lazy (infinite!, or length n if supplied) sequence of xs.” It has two arities, [x], and [n x]. If you don’t supply a length, make sure to terminate the sequence at some point with a take or another function which will not consume the entire (infinite) sequence.

Lets try it out:

(repeat 5 :a)
;; (:a :a :a :a :a)
(repeat :a)
;; Operation infinite exception
(take 5 (repeat :a))
;; (:a :a :a :a :a)
(repeat 5 [:a :b :c])
;; ([:a :b :c] [:a :b :c] ...)

That last one might not have been what you were expecting. To repeat each element in a collection you need to use…

Cycle

Cycle will return a “lazy (infinite!) sequence of repetitions of the items in coll.” This is what we wanted in our last example. Cycle doesn’t have an option to take a length, so we need to wrap it in take or it will carry on forever.

(take 5 (cycle [:a :b :c]))
;; (:a :b :c :a :b)

Iterate

Iterate is another lazy, infinite function that needs to be guarded with take. It takes a function and a starting value. It evaluates (f x), then calls f on the result of (f x), then calls f on the result of that result, and so on ad infinitum. The canonical example for this is generating arithmetic sequences.

(take 5 (iterate inc 3))
;; (3 4 5 6 7)

It’s not often seen in real world code, as evidenced by most of the GitHub search results for iterate being for solving 4Clojure problems.

Repeatedly

The documentation for repeatedly says:

Takes a function of no args, presumably with side effects, and returns an infinite (or length n if supplied) lazy sequence of calls to it

Clojure generally tries to avoid side effecting functions, so this one doesn’t get so much air time, but one usage is for generating a sequence of random numbers. Again as an infinite sequence it needs to be guarded with a take.

(take 5 (repeatedly (fn [] (rand-int 10))))
;; (6 2 5 2 1)

Constantly

Constantly takes a value, and returns a function which will always return that value. The function takes any number of arguments.

(def bfun (constantly :b))
;; #'user/bfun
(bfun)
;; :b
(bfun :lots :of :args)
;; :b

This is often used when you have a constant that you always want to use, but the API that you’re working with requires you to pass it a function, not a value. A real world example is in Reid McKenzie’s Pascal parser. I often get repeatedly and constantly confused, so don’t feel bad if you need to look up the documentation when you come across one of them!

Dotimes

Dotimes is the black sheep of the repeating function family, it’s the only one to use a binding expression. It returns nil, so it’s only really useful for side effecting functions. You give it a binding expression with a name and the number of repetitions you want, and it will execute the function with name bound to the repetition number. It starts counting from 0 and goes to repetition number - 1.

For example:

(dotimes [i 5]
  i)
;; nil
;; This doesn't have any side effects and we are just returned nil
(dotimes [i 5]
  (println i))
0
1
2
3
4
;; nil
;; i is printed to the console each time through the loop

Conditional read not allowed - Clojure Reader Conditionals

A new feature in the upcoming Clojure 1.7 release is Reader Conditionals. Reader Conditionals let you write portable Clojure/ClojureScript code in the same file, and wrap platform specific code in an expression which will only be executed on the platform it’s meant for.

A simple example is

(try
 (>! c msg)
 (catch #?(:clj Exception
           :cljs js/Object)
        e)
 .... ))

One important restriction on Reader Conditionals is that they can only be used in Clojure files with a new .cljc extension.

I was recently porting some code to use Reader Conditionals and got this error message when compiling the namespace with a Reader Conditional in the file:

CompilerException java.lang.RuntimeException: Conditional read not allowed, compiling: <filename.clj>

I was very puzzled by this. After thinking for a little bit I started deleting parts of the file, to see if there was something in it which was stopping Reader Conditional use. I got down to a bare namespace with just a single reader conditional in the whole file and it still wouldn’t compile. I looked at my project.clj file to see if there was a weird setting in there causing problems but that looked fine too.

Eventually I realised that this file didn’t have a .cljc extension. I’d converted some of the files when I started, but then started adding Reader Conditionals to one I hadn’t converted. After changing the extension from .clj to .cljc everything compiled fine and I was on my way again.

I opened CLJ-1734 to give more descriptive error messages for Reader Conditional failures. Feel free to vote for it if you think it would be a helpful feature.