Hacker News new | ask | show | jobs
by _halgari 3446 days ago
STM and concurrency...sure those are "aspects" of Clojure, Clojure's biggest strength is that it's data-centric. The vast majority of the language is focused around manipulating hashmaps, vectors, sequences, etc, and do that in an efficient way. All the concurrency stuff is just icing.

So I really have to sit back and shake my head when the author says he's going to be as good as Clojure, then goes off into the weeds with custom syntax, STM and actors. Really? Why actors?

This would have been a much better article if it just left Clojure out of the discussion since whenever the author talks about the language he's mostly wrong.

And as always, lies, damn lies, and benchmarks: https://benchmarksgame.alioth.debian.org/u64q/compare.php?la... If you're going to use a phrase like "faster", at least spend some time to define the word.

5 comments

> Really? Why actors?

Probably because Common Lisp is already on a similar level of being data-centric, so it would be redundant to talk about it. Actually, the only difference between how Clojure and CL treat data is that (while both offer access to both kinds of data) Clojure strongly prefers immutable values, while CL doesn't care.

Clojure doesn't just "strongly prefer" immutable data but syntactically ensures that all data is immutable unless governed by one of four concurrency mechanisms. Persistent data structures aren't just a popular convention in Clojure--the language is literally built around them.
I like Clojure. It's data-centricness is something that it shares with certain ways of programming in Common Lisp. In particular the use of lists in Common Lisp. For example, a list holding a schedule of appointments

  ;;; adapted from 
  ;;; https://www.ida.liu.se/ext/caisor/archive/1978/001/caisor-1978-001.pdf
  (((JAN 12 2014)
      ((9 15) (10 00) (SEE ANDERSON))
      ((10 45) (11 00) (SEE LUNDSTROM))
      ((13 15) (16 00) (ATTEND Y COMMITTEE MEETING)))
  (((JAN 13 2014)
    ((9 30) (10 00) (ATTEND NEW PRODUCTS PRESENTATION)))
A lot of what Clojure brings is more efficient abstractions. For example Common Lisp has two key-value stores a-list and hash tables that Clojure collapses into one thing and then rolls together with hashes and sets as much as possible.
Same data in Clojure:

    [{:appt/start #inst "01-02-2014T9:15:00Z" :appt/end #inst "01-02-2014T9:15:00Z" :appt/description "See Anderson"}
     {:appt/start #inst "01-02-2014T10:45:00Z" :appt/end #inst "01-02-2014T11:00:00Z" :appt/description "See Lundstrom"}
     {:appt/start #inst "01-02-2014T13:15:00Z" :appt/end #inst "01-02-2014T16:00:00Z" :appt/description "Attend Y Committee Meeting"}
     ...]

Let's point out some important differences:

1) Clojure prefers maps over cons cells. This means that I always know exactly what I'm looking at. I don't have to guess that the second times are the end-times...I know because it' named :appt/end.

2) We don't overload data types. Have a date? Use a date type. In the CL example we see symbols and numbers sometimes used for descriptions, sometimes for times, etc. Here in Clojure we have #inst ... which creates an actual DateTime object. Now I no longer wonder what I'm looking at, I know it's a date.

3) Clojure prefers data over DSLs. What this calendar example shows is some sort of domain specific language that not only has to be parsed by a program, but it also has to be parsed by a human.

4) Got a meeting description, use a actual string....what's up with the use of symbols as strings (I never understood that about CL).

5) From the perspective of a data modeler...in the CL example if you want a meeting to last over midnight or for longer than a day, it looks like you're sunk.

But thanks for the example, it's fun to see how data modeling is done in other languages.

I think most CL programmers would agree with several of your points. Using lists of symbols instead of strings is not useful. Though there are infinite ways to model the calendar, a slightly better way would be something like the following. Note that it's incredibly similar to your Clojure example, though it uses property lists instead of maps:

    ((:start @2014-01-02T09:15:00Z :end @2014-01-02T09:15:00Z :description "See Anderson")
     (:start @2014-01-02T10:45:00Z :end @2014-01-02T11:00:00Z :description "See Lundstrom")
     (:start @2014-01-02T13:15:00Z :end @2014-01-02T16:00:00Z :description "Attend Y Committee Meeting")
     ...)
Do note that this uses the local-time[1] library's timestamp syntax. If you don't want dependencies you could put numerical universal-time[2] timestamps there.

Of course, depending on the programmer, this would change a lot. For example you could use a simple list or vector of 3 items instead of a property list, or maybe an association list. But if this isn't a one-off thing, most would probably define a class or a struct to store the data in, with proper accessors to get the data out. This wouldn't be so easily PRINTable as the above, but in code it would look something like the following:

    (list (make-appointment :start @2014-01-02T09:15:00Z
                            :end @2014-01-02T09:15:00Z
                            :description "See Anderson")
          (make-appointment :start @2014-01-02T10:45:00Z
                            :end @2014-01-02T11:00:00Z
                            :description "See Lundstrom")
          (make-appointment :start @2014-01-02T13:15:00Z
                            :end @2014-01-02T16:00:00Z
                            :description "Attend Y Committee Meeting"))
Now you have data with actual types, with accessors like appointment-start, appointment-end, and appointment-description.

[1]: https://common-lisp.net/project/local-time/manual.html#Reade... [2]: http://www.lispworks.com/documentation/HyperSpec/Body/25_adb...

Historically, Common Lisp's preference for symbols over strings to represent text is due to the nature of equality comparisons. Symbols are equal based on their pointers. Strings are equal based on their byte sequences. Clojure leverages Java's immutable strings to allow pointer comparisons.

I apologize for not being clearer. The point I was trying to make is that Lisp programs often used lists in a data centric manner and my example based on the 1978 paper was optimistically intended to illustrate that.

Perhaps there's an analogy between lists in traditional Lisp programming and text in *nix systems in so far as each is used as a standard interface when composing systems from sub-systems. Or perhaps not.

> But thanks for the example, it's fun to see how data modeling is done in other languages.

Franky, this example upset me because this is the typical "LISP has only lists and symbols" example. On the first hand, the format is not deprecated: you could write a simple parser today and extract the data back. But on the other hand, it reinforces those core myths about Lisp having no real datatype, no strings, etc.

I wasn't trying to upset anyone. I came across the example in the cited document a few years ago and had an epiphany. What I find elegant is that it does not have all the conceptual overhead of a 'real' database even today. In historical context it does not have all the overhead of the primary alternative COBOL (there weren't widely available relational database systems in 1978). The other thing I found striking was that it was code I could write myself without reference to a pile of documentation.

I mean the parser is something like:

  (defun appt:appointment-date (appt)
    (first (first (appt)))
  (defun appt:appointment-start-time (appt)
    (first (first (rest appt))))
  (defun ...
In fairness, I don't really worry about winning the language war. Partly because I don't draw a big distinction between Clojure and 'Lisp', or at least not one that comes down to more than choosing one or the other based on the runtime that makes sense and the skills of the people writing the code.

Anyway, what I found interesting about the code years ago and yesterday was how lightweight it was. There's nothing inherently wrong with storing appointments in Zulu time, but if I'm talking with Lundstrom it's easier if we both say '10:45'. Reflecting that business logic in the data may make sense in the context. In other contexts it might not.

Thanks for the clarification, I don't mind the actual format and its simplicity; I understand what you mean about being lightweight. What bothers me is that I often see questions or statements about Common Lisp which contain factually wrong statements. The most notable is "Common Lisp has only lists (e.g. hash tables are built using cons cells)"; another one is "you have to write in All Caps". And your example is indirectly confirming that, as shown by the reply you got, saying "here is how we do in Clojure" and "it's fun to see how data modeling is done in other languages" (that was really depressing to read). Sorry for the "upset" part.

As an aside, since I can customize the readtable in CL (which is not considered useful in Clojure), I added a single entry for "#i" (and read-char to ensure it ends with "nst") to read the exact same data as in Clojure; but I got an error about invalid dates; it just happens that the Clojure example does not actually contain valid RFC3339 dates (I also tried to with Clojure => invalid date format).

Like I said, I appreciate Clojure and I took the comment as someone's expression of enthusiasm for the language. Part of the design rationale for Clojure is to improve the lives of Java programmers and Common Lisp isn't usually a viable option in such a context and hence there's less justification for a comprehensive analysis of its features. If someone is more or less committed to the JVM, the numerical sophistication of Common Lisp doesn't change the criteria for choosing a language very much, if at all. Like a lot of things, it depends on context.
Cl has fset for persisten collections
> And as always, lies, damn lies, and benchmarks

Things change: not the same JVM as 2014, not the same Clojure.

We may quote to one another with a chuckle the words of the Wise Statesman, lies, damned lies and statistics, still there are some easy figures which the simplest must understand but the astutest cannot wriggle out of. 1895 Leonard Henry Courtney

Isn't a functional language supposed to be function centric?
Sure, but what do those functions do? In clojure they manipulate data. That's pretty much the gist of the language: using pure functions to manipulate data.

Almost all the work I do these days as a software engineer is data transformation. Going from a HTTP request, which is data (even the header is a hashmap), and a HTTP body, which is data, into some business logic that eventually writes to a database in a different format.

Even the most complex systems I've built containing dozens of servers and multiple databases, queues, http servers, etc. All boil down to transforming data from format A to format B perhaps with conditional logic applied.

So yes, Clojure is a functional language, but functions are just a tool to be used to get the actual work done of transforming data.

Clojure's rich persistent data structures together with protocols facilitates functional programming in Clojure. When Clojure is called a data-centric I think most users mean that it encourages functional programming applied to rich immutable data.