17 comments

[ 3.0 ms ] story [ 48.9 ms ] thread
At the risk of stating the obvious, this article doesn't really talk about why they picked Clojure beyond one or two superficial reasons.
Yes, it seems more like an advertisement for recruiting.
it really isn't a recruiting advertisement, we only have R&D in Israel & Ukraine - but we will take it into account and add a drill down on the next article
It ends with "Want to write everything in Clojure? Join us"...
(comment deleted)
(comment deleted)
Everybody sees "Clojure" → click upvote → Read the article → regret it → look for downvote button → :( /me
Low signal-to-noise posts about Clojure are damaging to adoption because it doesn't say anything about why Clojure is a good option on the JVM.

Instead, I will try to give some reasons why Clojure is a good fit for high-performance stream fusion, which I presume AppsFlyer has to do a lot of:

- [Onyx](https://github.com/onyx-platform/onyx) for distributed, masterless, fault-tolerant data processing

- [core.async](https://github.com/clojure/core.async) for CSP-style events through a channel abstraction. I miss core.async so much in every other language, I generally end up implementing a subset of the semantics.

- [Transducers](https://clojure.org/reference/transducers) for composable transformations, e.g.

    (filter odd?) ;; returns a transducer that filters odd
    (map inc)     ;; returns a mapping transducer for incrementing
    (take 5)      ;; returns a transducer that will take the first 5 values of a sequence

    (def xf
      (comp
        (filter odd?)
        (map inc)
        (take 5)))

    (into [] xf (range 100)) ;; => [2 4 6 8 10]
Note that no intermediate data structures are created during these transformations, so it's super fast.

- [Specter](https://github.com/nathanmarz/specter) decomplects navigation and transformation, pulling apart the "where" from the "how". In most imperative languages whenever the shape or performance characteristics your data structures change, you also have to update all the code that transforms it. Not with Specter.

- Zach Tellman's [Manifold](https://github.com/ztellman/manifold) provides event-streaming abstractions on top of async workflows like lazy sequences, core.async and Java's BlockingQueues.

Wait! What? That's what 'transducers' means?

Being new to Clojure I've been avoiding some concepts until later, but if they hadn't renamed old concepts, ie: higher order functions to transducers, maybe newbies wouldn't get scared.

I’m not a Clojure programmer but from the example given, “transducers” would be equivalent to lazy evaluation / stream fusion.

Simple HOFs wouldn’t prevent the elements to be traversed in each subsequent function call.

That's not what transducers are, though. They're a "recipe" for a function composite. I'm simplifying things, but transducers compose to form a function that then executes against each element in the collection passed in. The main difference is that no intermediary data collections are created. There's more, and it's worth reading up on them.
I totally agree with the "missing core.async" part. Actually I miss them so much in python that I implemented the complete set of semantics in [aiochan](https://github.com/zh217/aiochan).

(disclaimer: I am the author of aiochan)

This article doesn't really say anything. Walmart is a much better example of "Why Clojure". Or anything that comes out of Rich Hickey's mouth, basically