How F# helped me to appreciate Clojure’s reduce to traverse data structures (XMLs)

I’ve no doubt that the book Real-World Functional Programming – With examples in F# and C# by Tomas Petricek and Jon Skeet will make me a better functional programmer and have often found it very enlightening. I haven’t done with its reading, but my meetup with it won’t finish once read. It’s too valuable to put on a bookshelf and let it get forgotten. Functional programming is so much easier with the book!

It’s about F# yet I’m reading it to understand functional programming better. F# is just another language. What matters is that it’s a functional language and its syntax doesn’t bother me. Moreover, I found the language highly intuitive and similar to Scala (they say it’s because they borrowed a lot from OCaml). I simply don’t mind learning yet another functional language provided it makes my foray into functional paradigm easier and faster. Even if it’s a non-JVM functional language.

On page 367, there’s a sample with Seq.fold to traverse a XPath-like path in a XML. I burnt a few brain cycles before I figured out how it works. It’s simple, but it wasn’t almost half a day before. That’s that sort of examples I enjoy the most. They’re usually short yet powerful and their understanding doesn’t come for free – you need to spend some time and think. Think a lot!

Here’s a session with Clojure REPL to have a version in Clojure.

;; Let's have a data structure - a map with other, nested maps
;; One could see it as an XML-like data structure.
;; I did.

user=> (def req (update-in {} [:a :b :c :d] (fn [v] "value")))
#'user/req
user=> req
{:a {:b {:c {:d "value"}}}}

;; Let's define a function xelem that accepts a key (a tag) and a map (a node).

user=> (defn xelem [s el] (s el))
#'user/xelem

;; Let's have some fun and play with the function.

user=> (xelem :a req)
{:b {:c {:d "value"}}}
user=> (xelem :b (xelem :a req))
{:c {:d "value"}}
user=> (->> (xelem :a req) (xelem :b))
{:c {:d "value"}}

;; I played until I found out the following:

user=> (->> req (xelem :a) (xelem :b) (xelem :c) (xelem :d))
"value"

;; It's that calling a key with a map in sequence gives you the value.

;; Let's define a XPath-like path to the value.

user=> (def path [:a :b :c :d])
#'user/path

;; With reduce life became so easy.
;; Or was it because of the book?!
;; I think both.

user=> (reduce (fn [v1 v2] (println "DEBUG v1:" v1 "v2:" v2) (xelem v2 v1)) req path)
DEBUG v1: {:a {:b {:c {:d value}}}} v2: :a
DEBUG v1: {:b {:c {:d value}}} v2: :b
DEBUG v1: {:c {:d value}} v2: :c
DEBUG v1: {:d value} v2: :d
"value"

;; The book "Real-World Functional Programming" uses the pipelining operator |>,
;; and Clojure provides the threading macros: -> or ->>

user=> (->> path
            (reduce (fn [xn s]
                      (xelem s xn)) req))
"value"

;; I like the book, F#, Clojure and learning!
Be Sociable, Share!
This entry was posted in Languages.

4 Responses to How F# helped me to appreciate Clojure’s reduce to traverse data structures (XMLs)

  1. Jason Hickner (@jhickner) says:

    this is a little easier:
    user=> (-> req :a :b :c :d)
    “value”

    • Well, you may have experienced it too when looking for a solution you usually find the easiest yet convoluted, not the smartest one :) Thanks for the hint! I think I won’t surprise you when I say I had known it, but was…sidetracked.

  2. There’s also the *-in functions:

    (get-in req [:a :b :c :d])

Leave a Reply

%d bloggers like this: