
Clojure to die for - cryptolect
http://cjohansen.no/clojure-to-die-for
======
cjohansen
I spent 10 or more hours writing "Building static sites in Clojure with
Stasis" ([http://cjohansen.no/building-static-sites-in-clojure-with-
st...](http://cjohansen.no/building-static-sites-in-clojure-with-stasis) /
[https://news.ycombinator.com/item?id=7375425](https://news.ycombinator.com/item?id=7375425)).
It was written so people could enjoy it even with little Clojure experience.
As a piece of supportive material, I pulled together this post about some (to
me) exciting idioms in about 40 minutes. Results after one day: the
afterthought is #4 on hacker news, and has 10k views. The "real" post has a
mere 3k views, and is long gone from hacker news.

Who says hard work pays off?

~~~
jimbokun
I suspect this is like the executive who said "I know over half of my
advertising dollars are wasted, I just don't know which half!" (pre-AdWords).

I don't think you could have predicted ahead of time which of those would be
more popular before writing them.

Having said that, the Stasis post is much longer, so it's less conducive to
reading at work and quickly dashing off a comment or two on Hacker News
without your boss noticing you goofing off. Hypothetically speaking, of
course. :)

~~~
dcotter
Perhaps an HN article analyzing which HN articles sink and which swim sometime
in the future?

------
worldsayshi
I've thought about learning Clojure but already knowing Haskell I feel that in
comparison I wouldn't gain much and then loose some.

~~~
brentshields
As someone who knew Haskell first and then learned Clojure, I can tell you
that this assumption is incorrect. It's so completely philosophically opposite
from Haskell in so many ways that it's a very good mental exercise to really
grok them both. It will make you appreciate how much you're paying for that
fantastic type system. Understanding this cost will allow you to make better
decisions in the future about which technologies are a better fit for a given
project.

~~~
TuringTest
Sir, your comment is intriguing and I want to hear more about it.

What is this philosophically opposite that you mention? Haskell is good at
creating domain-specific languages, so I think the differences must be around
the superior meta-programming facilities in lisp languages. So, what would you
do with Clojure that you couldn't do in Haskell easily?

~~~
brentshields
It's an oversimplification, but the basic philosophical difference from my
point of view is that Haskell values correctness above all and Clojure is
above all pragmatic. I think all of these major differences in the languages
are reflections of this:

\- static vs. dynamic typing

\- pure vs. allowing side effects

\- native vs. hosted

\- focus on syntax vs. rejection of syntax

Depending on the problem, you may get big benefits out of one side or the
other.

One particular thing that is much easier in Clojure is dealing with
heterogeneous, hierarchical data. This is just so effortless because of the
combination of dynamic typing and the wonderful built-in persistent data
structures. This is possible but takes a lot more work to do in Haskell and is
much slower to iterate on when your data changes.

~~~
nbouscal
I'm confused. In what sense is correctness not pragmatic?

Also, have you used lenses yet? Heterogenous hierarchical data in Haskell is
quite easy now.

~~~
wes-exp
_Guaranteed_ correctness has a cost in the form of extra specification needed
to get things done. For example, many type errors are essentially noise: they
do not catch real bugs, and they create needless busy-work for the programmer
to correct them.

example:

function stringify(FooObj object): return object.toString()

Now suppose we do some refactoring somewhere, and we want to call stringify
not with a FooObj but with a BarObj. Since we have defined stringify as taking
a FooObj, the compiler whines until we update the definition. However,
depending on your philosophy, this error is nothing more than noise, because
stringify would work fine on a BarObj.

Pragmatists may wish to forgo such safety in favor of greater flexibility:

function stringify(object): return object.toString()

This is the essence of liberal programming: an emphasis on flexibility and
minimal specification, at the cost of reduced safety guarantees.

~~~
nbouscal
That example is a straw man. First, all reasonably strong type systems support
polymorphism well enough to handle that case trivially. Second, type inference
is a thing, so a lot of code in strongly-typed languages even looks the same
as your second code example.

I'm not saying you can't come up with a fair example to support your argument.
I will say, though, that it's going to be a _lot_ harder to do so, and that
such examples only rarely come up in practice. The argument for flexibility at
the cost of guarantees of correctness used to be a good one, but it has
weakened significantly with time, and before long will cease to be valid at
all.

~~~
wes-exp
Terms like "straw man" imply that I'm trying to debate, but I'm not. You asked
how correctness could be contrary to pragmatism, and I tried to provide an
example.

~~~
nbouscal
Whether or not it's a debate has nothing to do with whether or not your
example is valid. I was just pointing out that it isn't.

~~~
wes-exp
Out of curiosity, how would you define my example stringify function in
Haskell with "no cost" safety around the argument type?

~~~
Tuna-Fish
The typeclass that provides "toString" in haskell is "Show". It defines a few
functions, of which the important one is "show", which takes the original type
and returns a string. A function that is equivalent to the java-ish example
is:

    
    
        stringify s = show s
    

No type definition is necessary, because the compiler can correctly infer the
correct, most generic type:

    
    
       stringify :: (Show s) => s -> String
    

Or, for any s in type class Show, a function that makes s into a String. Note
that dispatch is done statically.

Modern type systems eliminate most of the cost of type safety through good
generics and type inference. Mainstream statically typed languages are just
20-30 years behind the state of the art.

(note that the example actually can be reduced to: stringify = show)

------
vog
Small typo:

The link to Clojure points to
"[http://cjohansen.no/clojure.org"](http://cjohansen.no/clojure.org") instead
of "[http://clojure.org"](http://clojure.org").

------
brentshields
Nice writeup. There's an error in one of the examples though:

(map :name people)

evaluates to a lazy sequence, not a vector.

------
nardi
In which the author gets really excited about idioms in Clojure that don't
seem that exciting.

~~~
thom
I think the article is overly focused on small, pragmatic syntax issues
without explaining how the underlying language and runtime have _enabled_
them.

It might be interesting to discuss _why_ keywords or maps or sets can work as
functions, by implementing the IFn interface. Perhaps explain why
homoiconicity and macros enable things like thread-first and -last, and talk
about how we can implement our own reader literals.

~~~
cjohansen
> I think the article is overly focused on small, pragmatic syntax issues
> without explaining how the underlying language and runtime have _enabled_
> them.

That was kinda the whole point of the post.

