Learn Clojure using records and protocols

I am a recovering statically typed, object oriented Java programmer. I have never been object free for more than a few days, but my goals are higher. Clojure has opened my eyes.

Once I got over the whole parentesis thing, one of the first things I thought about while learning Clojure was “Ok, this REPL thing is nice, but how do I build larger programs?”. But since my mind was still object oriented, what I really was thinking was “How do I do encapsulation in something like classes?”. In this article I will focus on how to build larger structures in Clojure in a way similar to Java. This way I hope that you will see that learning Clojure is not that big leap as it might initially seem.

One thing I have realized is that in Java we use classes for many purposes. In a typical web application written in Java using something like Spring framework you will often find:

  • Data transfer objects (DTO:s)
  • Services (REST API, controllers, DAO:s)
  • Rich objects (if you’re lucky!)

A DTO is just a struct and contains no behavior. To minimize the boilerplate in Java I tend to implement DTO:s using public final fields. I like the fact that the DTO:s act as a schema. For example this acts as documentation of the output of a REST service. However, I have found that most client developers don’t care about the schema and are instead interested in sample data. Sometimes you also see DTO:s as part of database access. These DTO:s are sometimes referred to as anemic domain objects, but essentially they are the same.

A Service (or Spring Bean) is typically a singleton that contains some methods and some collaborators injected as fields using dependency injection. The different collaborators are other services that implement certain interfaces. Besides implementing an interface a Service only act as a placeholder which collects related methods and perhaps some shared private methods. A problem with the singleton pattern is that newbie developers doesn’t realize the consequences of sharing the same instance between multiple threads. They store state in private fields to avoid having to pass along one or more objects to several methods. Very convenient, but very wrong.

By Rich object I mean what you typically think of when you think of an object in an object oriented language. That is something that encapsulates some data and exposes some behavior related to this data. By the way, I don’t consider getters and setters as behavior! They also tend to break encapsulation. However, rich object classes is quite rare in most projects. Instead we have DTO:s as input and output to our services. We expose the database using a DAO that takes other DTO:s as input and output. I’m not saying that this approach is wrong, I just find it curious that while most of our application is procedural we still get very confused when trying to reason about building something like a web application in Clojure.

So how can we build things like this in Clojure? I will skip the whole discussion about state and focus on modularization and how you structure your code in a familiar way.

A DTO is just data and Clojure is very good at working with datastructures such as maps, lists and sets. The first time I realized that “Data as an API” was in fact a very good idea was at Kevin Lynagh’s presetation at Öredev. However, if you want something more like a struct Clojure provides something called a record. If you know Scala this is very similar to case classes. A record can be defined like this:

This creates an actual normal Java class Person with two immutable fields and implements hashCode and equals. A record also behaves like map which something you’ll appreciate after learning Clojure! Notice that although Clojure is dynamically typed you can add something called Type Hints if you need them:

Ok, so what about services and objects with behavior? Let’s divide the problem into grouping related functions, defining interfaces and finally dependency injection.

Unlike Java, Clojure provides different ways of grouping related functions. Which way you chose depend on what you want to accomplish. First of all, in Clojure a function does not have to declared inside a class. Instead they are grouped in namespaces, which is kind of like packages is Java. If you don’t have any particular requirements, then grouping functions in a namespace should be your first choice.

If you want to declare something like a class you must first declare its interface. This is done using something called a protocol. When learning Clojure you can think of this as the same thing as a Java interface. In fact, instead of using a protocol you can also use a normal Java interface for great interoperability. You create a protocol like this:

This is the same thing as the Java interface:

There are two things to notice:

  • You must include this in the argument list. In an object oriented language the object this is an implicit argument to all methods. When invoking a method you place the object before the method, something like person.sayHello(). Since Clojure is functional the method always goes first and then all the arguments, that is (sayHello person).
  • The method returns an Object. Since Clojure is functional we strive to be pure and to have no side-effects. A void method always means that there are side-effects!

Implementing a protocol can be done using a record like this:

And finally dependency injection. The short answer is that it is not as needed as in Java. I will try to get back to this topic some other time, but here are some good links:

Conclusion

Transitioning from Java to Scala was simplified by the fact that you could still write something similar to your old style Java code in Scala and then start learning the functional way and all other cool things available in Scala. When learning Clojure this threshold is higher as the language is so different than Java. However, by using records and protocols you should be able to do things in a similar way that you are already used to and then transition into more idiomatic Clojure.

7 Comments

  1. peter

    “I am a recovering statically typed, object oriented Java programmer. I have never been object free for more than a few days, but my goals are higher. Clojure has opened my eyes.”

    Always nice to read blogs of programmers that actually know how to write.
    ( English that is, that you know how to write programs too is just a bonus)

  2. anon

    Java style DI bullshit and their whole architecture in general doesn’t belong to the FP world . Especially the “service with injected collaborator services hiding behind interfaces” thing, this is absolutely the opposite side of the “composable tools doing something very simple” that is the functional programmming or also called the “unix philosophy” style.

  3. Robert

    Can you add an example of calling your sayHello function?

    • Jan Kronquist

      Something like this should work:

      (sayHello (->Person "Jan" "Kronquist"))

      This will create a new Person record with the firstName “Jan” and lastName “Kronquist”. Then call the sayHello function on that record.

Trackbacks for this post

  1. Clojure教程:Record和Protocol | IvanPig's Blog
  2. The farce known as “object oriented programming” | Smash Company
  3. Getting started with Storm Development on mac & contributing to open source Apache Storm. | Dungeon G33K

Leave a Reply