Live data from Hacker News

Clojure 1.7 is now available

blog.cognitect.com

41–50 of 125 posts

Re: Clojure 1.7 is now available

#41

>Transducers are composable algorithmic transformations. They are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Because transducers are decoupled from input or output sources, The biggest thing holding me back from learning Clojure is that I fear it will take me a decade to become remotely competent in it.

Transducers create functions that are meant to be passed to reduce (i.e. take accumulator and element and return a new accumulator).

Its possible to implement most other functions (map, filter, take etc.) using reduce. Transducers take advantage of that.

Unfortunately, if you try to do that, you'll notice that the implementation is sometimes tied with the way you build the entity: e.g. for vectors, map implemented in terms of reduce would start with an empty vector then append to it.

That sucks. We want operation chains that are independent of the data structure they operate on. We want them to work on vectors, lists, channels, whatever - anything that can have a reduce-like operation (anything reducible)

However, it turns out you don't necessarily have to recreate the entity (e.g. list) when chaining. All you need to know how to do is invoke the next operation in the chain, i.e. the next reduction function. For example:

* map can apply the function on the element to get a new result, then call the next reduction function with the accumulator and that new result.

* filter can check the element and either return the old accumulator or apply the next reduction function with the element, etc.

Therefore, transducers simply take an additional argument: the next reduction function that should be applied. This lets you build up a chain of operations (e.g. map filter etc) that doesn't care about the kind of entity they're operating on. Only the last "step" must be a reduction function that needs to know how to build the result.

Re: Clojure 1.7 is now available

#42
post #20
post #5

Earlier quoted context omitted.

It is one of the simplest and easiest languages around. The addition of transducers is an unfortunate case of Clojure "pulling a Haskell", valuing an elegant abstraction over ease of understanding and learning. Indeed, your comment alone shows that doing them (and especially giving them a high profile) was a mistake. Just because you can abstract something elegantly doesn't mean you should. No beautiful abstraction i…

I don't understand this argument. Clojure shouldn't have transducers because the word sounds scary? Programming language designers should avoid adding powerful, higher-order abstractions because they are hard to understand? This sentiment is incredibly anti-intellectual. And like you said, if you don't understand it you don't have to use it.

> Programming language designers should avoid adding powerful, higher-order abstractions because they are hard to understand?

In general? Absolutely![1] Making algorithms easier for humans to understand is the whole purpose of abstractions.

Programming is based two things: algorithms and abstractions, with algorithms being fundamental to computation and abstractions are usability features designed to help people write code -- that is their one and only purpose (computers and even theoretical computational models don't care about abstractions). Unlike algorithms, abstractions are not useful in isolation, and their utility is not a function of mathematical "power" but of psychological benefit. Their utility is measured by how much they help the human programmer write and read an algorithm[2]. Another way to look at it is that algorithms tackle essential complexity and abstractions tackle accidental complexity[3].

More specifically, abstractions help human programmers in two ways: they increase code reuse and improve code readability.

But here's the thing. There are things other than powerful abstractions that help humans program -- for example, a clear execution model (what happens when), debuggability etc.. This means that the more powerful (i.e. abstract) abstractions become they do not necessarily perform their function -- namely, assisting developers -- better. So every abstraction must be carefully weighed: how much wasted code does it save, how much more readable it makes code, vs. how much does it hurt understanding or debuggability.

I'd say transducers are just about the point where the abstraction starts hurting more than it helps. It's a borderline case. Now, I am not saying Clojure shouldn't have transducers (again, borderline), but that they most certainly shouldn't be emphasized.

> if you don't understand it you don't have to use it.

That doesn't quite work. I said, don't use them right away. Once a programming language and its libraries use an abstraction, you must learn it sooner or later. After all, most code you'll read isn't your own. This is why every language feature has a cost, and why good language developers are hesitant about introducing new features (transducers aren't a language feature, but they do have a prominent place in the standard library).

---

[1]: For example, Java and Go are both languages whose designers intentionally and radically reduced the use of many of the abstractions available in other popular languages of their time. Java in particular drastically removed abstractions possible by the most popular language at the time it was introduced, and attained tremendous success, partly because of that (well, at the time it was already apparent that C++'s power -- in addition to its lack of safety -- was greatly detrimental to code maintenance in most parts of the industry).

[2]: Abstractions are secondary to algorithms. Also note how almost all sub-disciplines of computer science deal with algorithms, and just one, rather small, discipline -- PL research -- is concerned with abstractions.

[3]: Even algorithms are often not judged in isolation; there are lots and lots of useful algorithms that aren't used because they are too hard to implement and maintain correctly -- regardless of abstractions used.

Re: Clojure 1.7 is now available

#43
post #20
post #5

Earlier quoted context omitted.

It is one of the simplest and easiest languages around. The addition of transducers is an unfortunate case of Clojure "pulling a Haskell", valuing an elegant abstraction over ease of understanding and learning. Indeed, your comment alone shows that doing them (and especially giving them a high profile) was a mistake. Just because you can abstract something elegantly doesn't mean you should. No beautiful abstraction i…

I don't understand this argument. Clojure shouldn't have transducers because the word sounds scary? Programming language designers should avoid adding powerful, higher-order abstractions because they are hard to understand? This sentiment is incredibly anti-intellectual. And like you said, if you don't understand it you don't have to use it.

Language design requires tradeoffs: the power of a feature, vs. how much more complex it makes the language to read/learn/etc. Making something easy to use doesn't commit one to anti-intellectualism; it just shifts the field of intellectual challenge elsewhere.

See Yegge's Perl essays[1] for examples of core-language features that are 'powerful' but problematic; hopefully that convinces you that at least the form of argument is legitimate. It could be that those Perl features are bad whereas transducers are great. I happen to share some skepticism of transducers because they overly-resemble existing features (partial application) and can be implemented pretty easy with existing Clojure tools. Also the marquee use case (sharing transformations between sequences and channels) so far seems exceedingly rare to me, though maybe things are moving in a direction where that's more important.

> And like you said, if you don't understand it you don't have to use it.

True to an extent, but (a) the high prominence given to the feature may well lead to a situation where you can't read other peoples' code without learning the concept, and (b) it's easy to create transducers by accident.

[1] e.g. ".." in https://sites.google.com/site/steveyegge2/ancient-languages-...

Re: Clojure 1.7 is now available

#44
post #20

Earlier quoted context omitted.

I don't understand this argument. Clojure shouldn't have transducers because the word sounds scary? Programming language designers should avoid adding powerful, higher-order abstractions because they are hard to understand? This sentiment is incredibly anti-intellectual. And like you said, if you don't understand it you don't have to use it.

Everyone who is learning Functional programming should be able to use map/reduce well. With that, understanding transducers is just natural.

Most languages with map/reduce don't have transducers (and they're certainly not central features). Also, don't forget that while Clojure is functional, it is also very much imperative (it's a functional-imperative language rather than a pure-functional language).

Re: Clojure 1.7 is now available

#45
post #9

It would be great to see a refactor of some code using transducers to get a better sense of what they're useful for. From an abstraction standpoint, I can see the attraction; but I am having a tough time imagining how it would improve code in practice.

Much Clojure code relies on applying nested transformations to sequences:

    (reduce + 0 (filter odd? (map inc (range 1000))))
Conceptually, this takes an input sequence of 1000 elements (range 100), then creates an intermediate sequence of (map inc), then creates an intermediate sequence of (filter odd?), then reduces that with +. (I am glossing over a number of internal optimizations - chunking and transients, but the idea holds.)

Transducers let you represent the transformation parts as an independent (reusable) thing:

    (def xf (comp (map inc) (filter odd?)))
You then apply that composite transformation in a single pass over the input:

    (transduce xf + 0 (range 1000))
transduce combines (a) input iteration (b) transformation application and (c) what to do with the results - in this case apply + as a reduce. Other functions also exist that make different choices about how to combine these parts: into collects results into a collection, sequence can incrementally produce a result, eduction can delay execution, core.async chans can apply them in a queue-like way.

There are a number of benefits here:

1. Composability - transformations are isolated from their input and output contexts and thus reusable in many contexts.

2. Performance - the sequence example above allocates two large intermediate sequences. The transduce version does no intermediate allocation. Additionally, some collection inputs are "internally reducible" (including range in 1.7) which allows them to be traversed more quickly via reduce/transduce contexts. The performance benefits of these changes can be dramatic as you increase the size of input data or number of transformations. If you happen to have your data in an input collection (like a vector), into with transducers can be used to move data directly from one vector to another without ever producing a sequence.

3. Eagerness - if you know you will process the entire input into the entire output, you can do so eagerly and avoid overhead. Or you can get laziness via sequence (although there are some differences in what that means with transducers). The upshot is you now have more choices.

4. Resource management - because you have the ability to eagerly process an external input source, you have more knowledge about when it's "done" so you can release the resource.

Re: Clojure 1.7 is now available

#46
post #44

Earlier quoted context omitted.

Everyone who is learning Functional programming should be able to use map/reduce well. With that, understanding transducers is just natural.

Most languages with map/reduce don't have transducers (and they're certainly not central features). Also, don't forget that while Clojure is functional, it is also very much imperative (it's a functional-imperative language rather than a pure-functional language).

> Most languages with map/reduce don't have transducers

That's true but mostly irrelevant to the point that transducer usage isn't complex once one understands map/reduce, which are common, whether or not transducers are.

Re: Clojure 1.7 is now available

#47

My only negativity with Clojure isn't really with the language itself, but with the Debian / Ubuntu packages, they're way outdated. Not sure who ever maintained them, or why they stopped doing so 1.4 being the last version. Outside of that for anyone wanting to check it out, you could try downloading LightTable and using ClojureScript, seems to be close enough I am able to use Clojure books with ClojureScript, not su…

I am reading several negative responses to this comment and I feel the need to throw him a hand. I like the idea of having a single place where every application is installed, to the extent possible. I like to try to install everything with apt-get, and I feel there is value in working that way. It's way more complicated to have apt-get for some things, lein for other ones, pip install, quicklisp, the ruby gems, emac…

One advantage to language specific packages is that IDE's have a uniform target across platforms (PyCharm has pip support for example).

That's a win if you switch around a lot.

Re: Clojure 1.7 is now available

#48

My only negativity with Clojure isn't really with the language itself, but with the Debian / Ubuntu packages, they're way outdated. Not sure who ever maintained them, or why they stopped doing so 1.4 being the last version. Outside of that for anyone wanting to check it out, you could try downloading LightTable and using ClojureScript, seems to be close enough I am able to use Clojure books with ClojureScript, not su…

Not sure where Ubuntu's at but Debian Stable (Jessie) has 1.6 available: https://packages.debian.org/jessie/clojure1.6

Re: Clojure 1.7 is now available

#49

>Transducers are composable algorithmic transformations. They are independent from the context of their input and output sources and specify only the essence of the transformation in terms of an individual element. Because transducers are decoupled from input or output sources, The biggest thing holding me back from learning Clojure is that I fear it will take me a decade to become remotely competent in it.

You may feel less intimidated by "Clojure for the Brave and True." The landing page features a rodent with an eye patch and a sword.

http://www.braveclojure.com/

Re: Clojure 1.7 is now available

#50

I'm really impressed by how backwards compatible it is. I just changed the Clojure version from "1.6.0" to "1.7.0" for one of my side projects, without updating any library versions, ran it, all the tests passed, and it seems to work perfectly. It also didn't break my Emacs setup, which is a breath of fresh air compared to how much work it was to get the Haskell tooling working with the new 7.10 GHC release (GHC-mod…

Yes this is something clojure users take for granted. I dont know the state of Scala right now but a year back even minor version bump was horrible in scala in terms of backward compatibility. Really impressive job by clojure code devs in terms of maintaining such stable releases.
Post reply on HN