The Clojure Sydney Meetup, Four Years In

In the beginning

Nearly 4 years ago I founded the Sydney Clojure User Group. I had been playing with Clojure for a little while and started a small study group to learn more. Initially this group was private and held at the ThoughtWorks office.

After a couple of meetups and conversations I decided to open up the group to the public and then realised we weren’t the only ones interested in Clojure and, even more important, interested in meeting like-minded people, share experiences and help each other.

Back then none of us used Clojure for anything serious. Apart from Steve. Steve is a brave man and was building his new startup on top of Clojure. This was a great source of inspiration and learning for all of us. Steve hired Harry and Harry told us first hand what using Clojure for work could be like.

For all of us hobbyist Clojurists, this was a great start.

Since then we’ve held 37 meetups with an average of 22.25 attendees per meetup. These numbers won’t impress many people, no doubt about that. What is impressive however is how it’s changed over the years.

Where are we at?

Last December I sent out a survey and the result for one of the questions made me smile. The question was: Do you currently use Clojure in your day job?

This is absolutely amazing. It’s great to see how much the community has matured. Nearly 50% of meetup attendees currently work with Clojure in Sydney! I’m fortunate to say I can include myself towards this milestone!

Going forward

We’re now in 2016 and the group shows no signs of slowing down. In fact, we’re picking up the pace a little and starting the new year with a new venue: from now on we’ll be meeting at Atlassian!

I would like to thank ThoughtWorks for all the support in starting and running the meetup. I wouldn’t have been able to do it if it wasn’t for them.

See you at the next meetup!

So Long 2015: Year Highlights

I should have published this post still in 2015 but as I was on holidays traveling around Australia I didn’t really have the time to do it. Better late than never, right? (also Linode - the provider hosting this site - has been under a severe DDoS attack so the site just came back up today)

2015 has been a unusually quiet year in this blog. Thankfully that is because I have been extremely busy in many other areas. I’d like to take the opportunity and look back at 2015’s highlights.

Atlassian

I started working with Clojure full-time at Atlassian and it’s been a great experience so far. One of the big things we’re working on is bringing collaborative editing to Confluence. However we had the opportunity to put our service to the test with the launch of Project Enso, an Atlassian Labs beta product. Check out this teaser video to get a quick feature run down.

Book

Back in March my book - Clojure Reactive Programming - was finally published. I have written about it before so I won’t dwell too much.

One last note on this though: you can get a digital copy of my book for only $5 as part of my publisher’s (Packt) Skill Up offer. Hurry, offer ends on the 8th of January!

Learning

I tend to do a mix of reading, courses and deliberate practice when learning new things. In terms of reading, you can have a look at a few things I read in my Good Reads - 2015 in books profile.

As for courses I decided to give MOOCs a try and am really happy with the courses I picked:

Coursera:

  • R Programming - This is a great source for anyone who’d like to get started with data processing. Even though R isn’t one of my favourite languages I cannot deny how easy it is to start making sense of your data.
  • Principles of Reactive Programming - As I have been into Reactive Programming myself for quite a while this gave me a bit of perspective on what Scala people mean by it. In particular the parts about Actor systems have been really valuable. Additionally the exercises are challenging and do drive the point home.
  • Introduction to Guitar - I have been playing the guitar for a long time now but I have never had any proper education on the matter. I decided to give it a go with this MOOC by Berkeley and am pleased with the results. Especially since I can now put names to things I have done for years :)
  • Leading People and Teams Specialisation - Leadership is a broad area and having been under both good and bad leadership I don’t want to make some of the same mistakes I’ve seen in the past. As such, I am seeking advice from multiple different sources - such as some of the books in my GoodReads profile above - as well as this specialisation. It has 5 courses in total. I’m two in and have enjoyed it so far.

Edx:

  • FP101x - Introduction to Functional Programming - I picked this course for two reasons: (1) it’s taught by Erik Meijer and I’m fan and (2) as I don’t get to write Haskell all that often I use these courses as practice so my Haskell doesn’t get too rusty. This is an excellent course though if you’re looking to get into functional programming.

I have also been dabbling more and more with Elm and PureScript. But more on that later. Maybe. :)

Speaking

Another good year in this area. Here’s what I spoke about this year:

Training

In case you don’t know one of my hobbies is weightlifting. Names such as Clean, Clean & Jerk, Snatches and Deadlifts are common in my day to day and 2015 has been a super year for my strength goals. In particular I’ve reached 120kg for my Squats and 140kg for my deadlifts which are my all time goals. For a bit of perspective I currently weigh 69kg.

These numbers are good but they are not elite level. However what makes them impressive - for me anyway - is that four years ago I injured my lower back and was diagnosed with Level 1 spondylolisthesis. My doctor at the time told me I wouldn’t be able to lift anymore and I was extremely down for quite a while. It wasn’t until I switched doctors and dedicated 100% to having proper form that I was able to reach a whole new strength level.

These results make me extremely happy. In case you’re into this sort of thing you can check a short video of my best lifts here.

That’s it

It’s been a great year. Let 2016 be even better!

Happy new year and keep on kicking ass :)

Clojure Reactive Programming Has Been Published

I’m extremely happy to let everyone know my book, Clojure Reactive Programming, has finally been published!

You can get it at the publisher’s website or on Amazon. I had a great time writing it and I truly hope you find it useful!

I’ve met a few authors here and there and I heard more than once that a book is never really finished. I now know what they mean.

The book doesn’t cover everything I wanted to write about due to time and space limitations. Having said that, now that the book is out I do plan to expand on a few things using this blog.

Stay tuned!

Thanks to everyone who gave me feedback on early drafts of the book! :)

So Long 2014: Year Highlights

Another year is about to end and as it is tradition in this blog it’s time to look at the year’s highlights!

Functional Programming

I have been advocating Functional Programming and Clojure for a while now and 2014 has been good to me. It was the first year where I was paid to work almost entirely in functional languages. Most of it was in Scala and a little less in Clojure.

I did have a short 8-week gig in Javascript but even then I tried to make it as functional as possible - e.g.: making use of Reactive Programming techniques (Rx-style)

I’m also thankful for the opportunity to have organised the first Sydney edition of ClojureBridge, a workshop aimed at increasing diversity in the Clojure community by delivering free workshops for women. You can read more about what we did on the day here.

Another bit of exciting news is that I’ve decided to quit ThoughtWorks. Starting in January I’ll be doing Clojure full-time at Atlassian. I’ve written about it here.

I’m looking forward to a very functional year!

Writing

Early this year I was invited by Packt Publishing to write a book on Reactive Programming in Clojure. I’ve announced it before in this blog but it is definitely a highlight of this year.

The book has been a while in the making and I had no idea of the amount of effort involved in writing one. I’m mostly content-complete now but working through a number of useful bits of feedback I got from early reviewers. I expect to be done with these in January and then the book should go into production.

I’ll keep everybody posted :)

Reading

Most of what I read this year - with the little free time I had - were academic papers and I would like to highlight a few that I particularly enjoyed:

Additionally I can’t help myself and recommend that you read Functional Programming in Scala if you have any interest in FP and Scala. Rúnar and Paul have done a great job and put a lot of good advice in this book.

Speaking

Between work and writing the book I’m happy I managed to speak this much:

Content

Here’s the Top 5 posts from this blog in 2014:

If this tells me anything it’s that I need to blog more!

Happy new year! Here’s to an amazing 2015!

A New Chapter Begins

Thanks, ThoughtWorks

After 4 years and 8 months I have made the hard decision to change jobs: Friday, 19th of December, was my last day at ThoughtWorks.

In this time I have made many friends and have grown a lot as a professional. Consulting presents so many new challenges that often times the technical problems you are trying to solve are the easiest part of the whole project. I am thankful for that.

I am also thankful for the opportunities I’ve had.

A few years ago I started dedicating most, if not all, my free time to learning Functional Programming. ThoughtWorks supported and encouraged my endeavours in doing so and that is a big part of enabling me to create and grow the Clojure community in Sydney through #cljsyd.

Additionally ThoughtWorks has also recognised the importance of Functional Programming in modern software development and allowed me to work almost exclusively in FP languages such as Scala and Clojure for the past year and a half.

About three years ago I wrote a post detailing my first year at ThoughtWorks. Much of it is still true so there is no point in repeating it. ThoughtWorks remains a great place to work.

What’s next?

My next challenge will be at Atlassian, an Australian Software company best known for being the creators of JIRA, Confluence, Stash and many other products.

Over the years I have met several Atlassians - that’s what they call themselves - and many of them have been heavily involved in the Functional Programming community. So when I was contacted by a friend saying Atlassian needed an experienced Clojure developer I was intrigued.

A few months ago Atlassian acquired WikiDocs, a startup whose business is to build technology that enables real-time collaborative editing. It turns out that this product is developed in Clojure and Clojurescript. That’s essentially what I’m going to be working on - I’ll write more about it once I settle in.

When I started learning Clojure over 3 years ago, little did I know that I’d be working full-time with the language so soon and I am super excited about it. It’s great to see that much effort paying off like this.

My start date at Atlassian is the 5th of January and I am looking forward to meeting a bunch of great people and making new friends.

ClojureBridge Sydney, Vol. I

This past weekend - 19-20th of December - a group of highly motivated individuals gave up their Friday night and their entire Saturday simply to learn how to code in Clojure!

It was the first ever ClojureBridge edition in Sydney and if you haven’t heard of it before, its goal is to increase diversity in the Clojure community. It does so by offering free workshops targeted at beginners.

You should totally read Julian Gamble’s post about the event for details. He’s was one of the volunteers on the day and has done a great job of describing his experiences.

I just wanted to take the opportunity to highlight and thank all volunteers who also gave up their time for free to help and coach 24 people eager to learn Clojure. They are: Alexandra Luca, Claudio Natoli, Julian Gamble, Marcin Nikliborc, Navin K, Scott Robinson, Svetlana Filimonova & Vineeth Varghese.

Without you, the event would not have happened!

A big thank you to our main sponsor - ThoughtWorks - who provided the venue, food, drinks and, of course, Clojure cupcakes.

Another big thank you to Atlassian who gave some cool gifts to our attendees! (Sunnies and hats!).

Last but not least, a huge thank you to all attendees who were absolutely stellar and made the event an amazing experience!

I had a blast organising and running the event and hopefully we will have a new edition soon! I hope it ignited the desire to learn even more about Clojure and Functional Programming!

Happy holidays!

λ♥

EuroClojure 2014 and Announcing My Book

A bit late for a EuroClojure 2014 post but I suppose “better late than never” applies here.

The best part of every conference is the networking. Meeting new and interesting people is priceless and I did plenty of that - mostly over Polish beer and food. Polish beer isn’t the greatest but if you must have it, better stick to Żywiec. It’s widely available and was the one which didn’t give me a headache :) - I’ve been told there are amazing microbreweries though I didn’t get a chance to try any while in Krákow.

The food on the other hand was excellent every single time. But I digress.

There’s no point in me describing the talks I watched as someone else has already done a much better job of it: it’s all in this gist by Philip Potter.

I gave a talk titled Taming Asynchronous Workflows with Functional Reactive Programming. You can check out the slides here. The video will be available in this link soon. I’ve received a lot of great feedback on it both at the event and afterwards through different channels. I’m really happy with how it turned out.

In this talk I mentioned publicly for the first time* that I am working on a book called Clojure Reactive Programming to be published by PacktPub. It seemed appropriate to announce it here as well.

As of yet there’s no set date but I’m spending most of my free time working on it. I’d say I have 55% of it done. Feel free to ping me directly if you’d like to know more. Alternatively you might want to follow the twitter account I created for the book.

There’s not much there yet but I’ll try and post book updates somewhat regularly.

See you around.

* Strictly not true as I’ve announced it before but this was the first time for a wider Clojure audience and the first time captured on camera so there’s no denying now ;)

Functional Composition With Monads, Kleislis and Functors

I’ve been learning Scala for my current client project and I find writing to be a great tool to test my understanding of any given topic. This means there might be a few Scala posts coming up soon as I keep learning interesting things.

Today I’ll be exploring a few different ways in which you can compose programs. I’ll be using Scalaz in this post.

The examples that follow all deal with Vehicles - more specifically makes and parts:

1
2
3
4
5
  import scalaz._, Scalaz._
  import scalaz.Kleisli._

  case class Make(id: Int, name: String)
  case class Part(id: Int, name: String)

Next we have a couple of functions which interact with these case classes:

1
2
3
4
5
  val make: (Int) => Make = (_) => Make(1, "Suzuki")

  val parts: Make => List[Part] = {
    case Make(1, _) => List(Part(1, "Gear Box"), Part(2, "Clutch cable"))
  }

So we have a function from Int to Make and then a function from Make to List[Part]. From set theory we know this implies we must have a function from Int to List[Part]. This is nothing more than simple function composition:

1
2
3
4
5
6
7
8
  val f = parts compose make
  f(1)
  // List[Part] = List(Part(1,Gear Box), Part(2,Clutch cable))

 // alternatively you can use 'andThen' which works like compose, but with the arguments flipped:
 val g = make andThen parts
 g(1)
 // List[Part] = List(Part(1,Gear Box), Part(2,Clutch cable))

Pretty boring stuff.

A more realistic example accounts for failure in our functions. One way we can encode this is using the Option data type:

1
2
3
4
val make  = (x: Int) => (x == 1).option(Make(1, "Suzuki"))

val parts = (x: Make) =>
  (x.id == 1).option(NonEmptyList(Part(1, "Gear Box"), Part(2, "Clutch cable")))

Now we have a function make: Int => Option[Make] and a function parts: Make => Option[NonEmptyList[Part]]. Based on our first example we should have a way to create a function from Int to Option[NonEmptyList[Part]]. This isn’t immediately obvious however.

While make does return a Make, it is wrapped inside an Option so we need to account for a possible failure. This leads to our first attempt:

1
2
3
4
5
6
  val f: Option[Make] => Option[NonEmptyList[Part]] = {
    case Some(m) => parts(m)
    case _ => None
  }
  val g = f compose make
  g(1) // Some(NonEmptyList(Part(1,Gear Box), Part(2,Clutch cable)))  

While this works, we had to manually create the plumbing between the two functions. You can imagine that with different return and input types, this plubming would have to be rewritten over and over.

All the function f above is doing is serving as an adapter for parts. It turns out there is a couple of ways in which this pattern can be generalised.

Monadic bind

Option is a monad so we can define f using a for comprehension:

1
2
3
4
5
  val f = (x: Int) => for {
    m <- make(x)
    p <- parts(m)
  } yield p
  f(1) // Some(NonEmptyList(Part(1,Gear Box), Part(2,Clutch cable)))

Which is simply syntactic sugar for:

1
2
3
4
5
6
7
8
9
val g = make(_:Int) flatMap (m =>
    parts(m).map(p => p))
g(1)
// Some(NonEmptyList(Part(1,Gear Box), Part(2,Clutch cable)))

// you can also use the symbolic alias for 'bind', which makes it a lot nicer
val h = make(_:Int) >>= parts
h(1)
// Some(NonEmptyList(Part(1,Gear Box), Part(2,Clutch cable)))

The reason this is better is that make and parts could operate under a different monad but the client code would not need to change. In the example below, we’re operating under the List monad:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val words: (String) => List[String] = _.split("""\s""").toList
val chars: String => List[Char] = _.toList

val f = (phrase: String) => for {
  m <- words(phrase)
  p <- chars(m)
} yield p

f("Motorcycles are fun to ride!")
// List(M, o, t, o, r, c, y, c, l, e, s, a, r, e, f, u, n, t, o, r, i, d, e, !)

// or even:
val g = words(_:String) flatMap (w =>
    chars(w).map(c => c))
g("Motorcycles are fun to ride!")
// List(M, o, t, o, r, c, y, c, l, e, s, a, r, e, f, u, n, t, o, r, i, d, e, !)

We used the exact same for comprehension syntax to compose these operations. This works because both Option and List are monads.

Notwithstanding, this still feels like unnecessary plumbing. All we are doing with the for comprehenstion / flatMap is extracting the values from their respective monads to simply put them back in. It would be nice if we could simply do something like make compose parts as we did in our first example.

Kleisli Arrows

A Kleisli arrow is simply a wrapper for a function of type A => F[B]. This is the same type of the second argument to the monadic bind as defined in Scalaz:

1
bind[A, B](fa: F[A])(f: A => F[B]): F[B]

By creating a Kleisli arrow from a function, we are given a function that knows how to extract the value from a Monad F and feed it into the underlying function, much like bind does, but without actually having to do any binding yourself.

To use a concrete example, let’s create a kleisli arrow from our parts function:

1
2
kleisli(parts)
// scalaz.Kleisli[Option,Make,scalaz.NonEmptyList[Part]]

You can read this type as being a function which knows how to get a value of type Make from the Option monad and will ultimately return an Option[NonEmptyList[Part]]. Now you might be asking, why would we want to wrap our functions in a kleisli arrow?

By doing so, you have access to a number of useful functions defined in the Kleisli trait, one of which is <==< (aliased as composeK):

1
2
3
4
  val f = kleisli(parts) <==< make
  // same as   kleisli(parts) composeK make

  f(1) // Some(NonEmptyList(Part(1,Gear Box), Part(2,Clutch cable)))

This gives us the same result as the version using the for comprehension but with less work and with code that looks similar to simple function composition.

Not there yet

One thing that was bugging me is the return type for parts above:

1
Make => Option[NonEmptyList[Part]]

Sure this works but since lists already represent non-deterministic results, one can make the point that the Option type there is reduntant since, for this example, we can treat both None and the empty List as the absence of result. Let’s update the code:

1
2
3
4
5
6
val make  = (x: Int) => (x == 1).option(Make(1, "Suzuki"))

val parts: Make => List[Part] = {
  case Make(1, _) => List(Part(1, "Gear Box"), Part(2, "Clutch cable"))
  case _ => Nil
}

It seems we’re in worse shape now! As before, parts’s input type doesn’t line up with make’s return type. Not only that, they aren’t even in the same monad anymore!

This clearly breaks our previous approach using a kleisli arrow to perform the composition. On the other hand it makes room for another approach: Functor lifting.

Lifting

In Scala - and category theory - monads are functors. As such both Option and List have access to a set of useful functor combinators. The one we’re interested in is called lift.

Say you have a function A => B and you have a functor F[A]. Lifting is the name of the operation that transforms the function A => B into a function of type F[A] => F[B].

This sounds useful. Here are our function types again:

1
2
make: Int => Option[Make]
parts: Make => List[Part]

We can’t get a function Int => List[Part] because make returns an Option[Make] meaning it can fail. We need to propagate this possibility in the composition. We can however lift parts into the Option monad, effectively changing its type from Make => List[Part] to Option[Make] => Option[List[Part]]:

1
2
3
val f = Functor[Option].lift(parts) compose make
f(1)
// Some(List(Part(1,Gear Box), Part(2,Clutch cable)))

f now has the type Int => Option[List[Part]] and we have once again successfully composed both functions without writing any plumbing code ourselves.

Mark pointed out to me that lift is pretty much the same as map but with the arguments reversed. So the example above can be more succintly expressed as:

1
2
3
val g = make(_:Int).map(parts)
g(1)
// Some(List(Part(1,Gear Box), Part(2,Clutch cable)))

Summary

If you are only now getting to this whole Functor/Monad/Kleisli thing this was probably quite heavy to get through. The point I am trying to make here is that learning at least some of the abstractions provided by Scalaz is certainly worthwhile. They encode common patterns which we would otherwise keep re-writing a lot of the time.

Special Thanks to Mark Hibberd who kindly reviewed an early draft of this post.

Validation and Internationalization in Clojure With Bouncer & Tower

I released bouncer in April last year and since then it has had a small but steady growth in usage.

So much so that I received some community feedback in the form of emails and pull requests which is simply great!

The latest and most substantial pull request, submitted by Vadim Platonov, added the ability to customise validation messages in anyway you like, as you can see in the section Internationalization and customised error messages of the README.

However the documentation only gives a simple example and while I believe it should show users how this opens up several different usage patterns, I thought I’d expand that in this post and integrate bouncer with the excellent Internationalization library tower to show how validation and I18n could work together in this configuration.

If you’ve never used bouncer before, I’d recommend you have a quick look at the Basic Validations section of the README before continuing.

Setup

The first thing you’ll need to do is create a new leiningen project and add both bouncer and tower as dependencies in your project.clj:

1
2
[bouncer "0.3.1-beta1"]
[com.taoensso/tower "2.0.1"]

Next, in your core namespace - or wherever, really - require both libs and set up a dictionary to be used in the examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(ns bouncer+tower.core
  (:require [taoensso.tower :as tower
             :refer (with-locale with-tscope t *locale*)]
            [bouncer.core :refer [validate]]
            [bouncer.validators :as v]))

(def my-tconfig
  {:dev-mode? true
   :fallback-locale :en
   :dictionary
   {:en
    {:person  {:name {:required "A person must have a name"}
               :age  {:number   "A person's age must be a number. You provided '%s'"}
               :address {:postcode {:required "Missing postcode in address"}}}
     :missing  "<Missing translation: [%1$s %2$s %3$s]>"}
    :pt-BR
    {:person  {:name {:required "Atributo Nome para Pessoa é obrigatório."}
               :age  {:number   "Atributo Idade para Pessoa deve ser um número. Valor recebido foi '%s'"}
               :address {:postcode {:required "Endereço deve ter um código postal"}}}
     :missing  "<Tradução ausente: [%1$s %2$s %3$s]>"}}})

Also, we need someting to validate so go ahead and create a map representing a person:

1
(def person {:age "NaN"})

Customising bouncer

Since 0.3.1-beta1, bouncer’s validate function receives an optional first argument called message-fn. As the name implies, it is a function from error metadata to, most likely, a string. This is our opportunity to use tower’s features to translate our error messages.

In order to accomplish that, we’ll be using this message function:

1
2
3
4
5
6
7
8
9
10
11
12
(defn message-fn
  "Receives a locale, tscope and a map containing error metadata.

  Uses this information to return a I18n'ed string"
  [locale tscope {:keys [path value]
                  {validator :validator} :metadata}]
  (let [tr-key (->> (flatten [path validator])
                    (map name)
                    (clojure.string/join "/")
                    keyword)]
    (with-tscope tscope
      (t locale my-tconfig tr-key value))))

In order to understand the function above, let’s validate the person map using identity so we can inspect the error metadata that will be the third argument to this function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(validate identity
          person
          :name v/required)

;; [{:name
;;   ({:path [:name],
;;     :value nil,
;;     :args nil,
;;     :metadata
;;     {:optional false,
;;      :default-message-format "%s must be present",
;;      :validator :bouncer.validators/required},
;;     :message nil})}
;;  {:age "NaN",
;;   :bouncer.core/errors
;;   {:name
;;    ({:path [:name],
;;      :value nil,
;;      :args nil,
;;      :metadata
;;      {:optional false,
;;       :default-message-format "%s must be present",
;;       :validator :bouncer.validators/required},
;;      :message nil})}}]

As we’re only validating the name in this case, that’s all we get in the return value of validate. However you can see how we have all sorts of useful information now - hopefully this makes the message-fn code above easier to understand.

Take it for a spin

We’re now ready for a couple of examples, using two different locales. Let’s get on with it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(validate (partial message-fn :en :person)
          person
          :name v/required
          :age  v/number
          [:address :postcode] v/required)


;; [{:address {:postcode ("Missing postcode in address")},
;;   :age ("A person's age must be a number. You provided 'NaN'"),
;;   :name ("A person must have a name")}
;;  {:age "NaN",
;;   :bouncer.core/errors
;;   {:address {:postcode ("Missing postcode in address")},
;;    :age ("A person's age must be a number. You provided 'NaN'"),
;;    :name ("A person must have a name")}}]

Now let’s get some messages in portuguese, shall we?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(validate (partial message-fn :pt-BR :person)
          person
          :name v/required
          :age  v/number
          [:address :postcode] v/required)


;; [{:address {:postcode ("Endereço deve ter um código postal")},
;;   :age
;;   ("Atributo Idade para Pessoa deve ser um número. Valor recebido foi 'NaN'"),
;;   :name ("Atributo Nome para Pessoa é obrigatório.")}
;;  {:age "NaN",
;;   :bouncer.core/errors
;;   {:address {:postcode ("Endereço deve ter um código postal")},
;;    :age
;;    ("Atributo Idade para Pessoa deve ser um número. Valor recebido foi 'NaN'"),
;;    :name ("Atributo Nome para Pessoa é obrigatório.")}}]

In case you’re too lazy to do this all from scratch, I created a github repo containing this example ready for you to play with. Go get it.

Conclusion

Hopefully this gives you a little taste of what you can do with the latest version of bouncer. Remember this is but one of many ways you could integrate the library so get creative :)

Once again, thanks to Vadim Platonov for submitting this pull request.

So Long 2013: Year Highlights

I finally got some free time so sit down and write this post. I was travelling with my girlfriend around the state of Victoria, Australia - mostly around the Great Ocean Road. I had an amazing time and it’s a trip I highly recommend anyone do.

As for this year’s highlights, let’s get started:

Clojure

clj-syd - the Sydney Clojure User Group - continues going strong and this is in great part thanks to the amazing community behind it. You can have a look at our wiki page for 2013 to see what we’ve been up to.

A major highlight is the fact that we brought Ambrose Bonnaire-Sergeant - creator of core.typed - to Sydney to give a talk on his creation. You can read a summary of the talk on our mailing list.

On a more personal note I’ve been basically living in Melbourne for the past ~6 months working on a full-time Clojure project. I have learned a lot and can’t wait to share a whole lot with the group once I’m back in Sydney.

Speaking

I was fortunate enough to speak at a couple of really cool events this year as well as a few user group talks. Here they are:

I have also attended Clojure/West in Portland back in March. Great conference, great content and amazing people. I put together some slides with my notes on SlideShare.

Books

2013 was a great year for Clojure as we saw quite a few new books be released during the year. I’ll briefly mention a couple:

Clojure Cookbook

As the authors describe it: “Clojure Cookbook marks Clojure’s entry in O’Reilly’s prestigious Cookbook Series. The book will contain hundreds of real-world problems and solutions, ranging from basic utilities to rich web services to heavy data processing.”

I have contributed three recipes to this book so I’m quite happy to see them make it in. You can buy the early digital edition from O’Reilly’s website.

The book is the main effort of authors Luke Vanderhart and Ryan Neufeld with contributions from the Clojure community around the globe. They’ve put together quite the selection.

Clojure Recipes

Shameless plug of one of #cljsyd’s regulars, Julian Gamble, who’s been working on this for a while. The book should be released at the end of January 2014 and I’m looking forward to it.

Clojure High Performance Programming

I’ve only just started reading it but I already like what I see enough to mention it here.

Content

Here’s the Top 3 posts from this blog in 2013:

Non-tech news

For those who know me my passion for sports - such as martial arts, rock climbing and strength training - is no secret and in 2013 I became a certified personal trainer in Australia. I did this mostly for personal development and to better my own training. The certification is already paying off. It was a great course.

Happy new year! Here’s to an even better one ahead of us!