Hacker News new | ask | show | jobs
by rlander 3854 days ago
Let me just preface this by saying that I've written Clojure in anger for the past 5 years and I agree with all of the author's points.

I love writing Clojure(Script) code, but I hate reading it. It fits perfectly with the way I think (when I'm writing code), but horribly with the way I try to understand code I wrote months ago.

With that said, the emergence of Elm as a viable alternative to front end functional programming has pushed all my new projects away from ClojureScript. Now, for the author's CS complaints, Elm has:

* Fast compile times, startup time indistinguishable from JS.

* Great error messages.

* Imature tooling, but that's changing quickly.

* Static type checking.

So, maybe op should give Elm a try.

[Edit: clarification]

2 comments

I was going to have a section on Elm, and then the post got too long :) Clojure(script) feels much more mature, and general-purpose. Elm is client-only, not nearly as many libraries, etc.

Would clojure's readability be helped by having a non-lisp pretty-printer? I've been thinking about this a bit - s-exps are great for writing, but fails to take advantage of our advanced visual pattern matching when reading it.

Cool, do write about your Elm experiences, I'd be interested to read!

Unfortunately, yes, Elm is restricted to the browser. But this might change in the future. And although it is somewhat tedious to write ports, IME Elm communicates better with JS than ClojureScript. You can just drop any old JS library and write a port for it (it will be async, though).

Sexps alone don't bother me at all. What bothers me (and I only came to this conclusion recently, after reading this[0]) is the interplay between functional code and the absence of type annotations. Sometimes it feels like Lisp just wants you to write clever code, all the time. Clever Python code looks bad. Clever Lisp code looks pretty nice.

[0] http://elbenshira.com/blog/the-end-of-dynamic-languages/

It's interesting to hear from someone else who also finds that Clojure/Lisp code can be difficult to read. I worked with Clojure professionally for 2 years and had started to think that I was the odd one out since everyone else I worked with was just gushing over the readability of the code. At times it could be easy to read, but very often I found it hard to follow and would have to spend more time then others deconstructing what was happening.

Which is not to say that I didn't like working with Clojure. I very much did. But it always seemed to be such a chore to read others code or code I hadn't seen in a few months or more. I remember on occasion showing some snippets of code to a co-worker and commenting that I thought there was too much "clever" code in there, that it was too dense and hard to follow. The co-worker didn't agree with me though, and I guess I just resigned myself to the idea that I was the odd one out here or something.

Ultimately, I think I ended up coming to the same conclusion as you. My way of thinking (and reading code apparently) just clicks so very much better with typed languages.

(I hope no one reads my post and treats this as some kind of attack on Clojure/Lisp and s-exps. It's not. I'm just noting that the style just doesn't jive with certain types of people. And I gave it plenty of time (2 years as noted above) before coming to that realization.)

And I thought I was the oddball ;)

Yup, sometimes it's easier for me to understand somebody else's OCaml/F#/Elm code than Clojure code written by myself. I still love working with Clojure, though. Go figure...

what aspects of the text made the code hard to read for you ? Was it the parens ? If LISP was displayed in the editor without parens (but still had them behind-the-scenes), so that the code was formatted much like Python, then would it be acceptable ?
I just listened to this podcast[0], and there's a really great discussion in there about when it's nice to have types, and when it's more trouble than it's really worth.

That's why javascript's flow[1] appeals to me so much, and I'm really interested in core.typed.

[0] http://giantrobots.fm/172 [1] http://flowtype.org

Love that podcast. I'll check it out later today, thanks.

About core.typed, just be aware that it comes with its own set of issues[0]. I've been down that road and it wasn't great.

[0] http://blog.circleci.com/why-were-no-longer-using-core-typed...

> You can just drop any old JS library and write a port for it (it will be async, though).

Actually, it's pretty simple to wrap Javascript functions so that you can call them directly from Elm. See e.g. [0] which is called from Elm here [1].

[0] https://github.com/elm-lang/core/blob/master/src/Native/Colo...

[1] https://github.com/elm-lang/core/blob/master/src/Color.elm

Elm's reactive model is best suited to UI's in my opinion. There's no good way to limit the data coming at you in a push-only model, so if you can't keep up with the incoming data flow bad things happen. On the UI this case usually isnt encountered, but on the server one needs mechanisms to deal with this
It can also happen on UIs:

Imagine you subscribe to lots of realtime data sources (e.g. via websocket), that deliver lots of updates to your UI. Then you have a race condition whether you can draw the updates faster than you receive them.

In an eventloop environment like Javascript it could be self-regulating (http/ws stack wouldn't receive new frames until you return from drawing. But it might also be not (because a C++ background thread in your browser receives the messages and queues them until they can be pushed to JS) - then you might get out of memory. But I totally agree that it's an exception for UIs to deal with such scenarios, but it should be normal an a server.

I went through the same debate with myself earlier this year, and finally decided to go with Elm. But, 100% of my work is client-side web, and has been for 15 years, so having a tool specialized in that field makes sense for me.
> non-lisp pretty-printer?

Brandon Bloom's fipp[1] works fine but the Chrome Canary dev tools have support for object formatters. I get reasonable results on .170 just doing a (js/console.log a-map).

[1] https://github.com/brandonbloom/fipp

>I love writing Clojure(Script) code, but I hate reading it. It fits perfectly with the way I think (when I'm writing code), but horribly with the way I try to understand code I wrote months ago.

I've used Clojure for 2 years - I feel the same way. It's a wonderfull language that exposed me to great ideas and showed me that simple things can be simple but boy does it get nasty when things don't fit the provided primitives and actually start getting complex - the language has no tools to deal with complexity it can't simplify.

Also a - for CLJS from me is that they haven't abstracted out the (do ...) macro from channels (ironic with all those talks harping on how simplicity is about disentangling things). The biggest pain in JS/DOM ecosystem is shitty semantics, poor standard library and callback stacks. ClojureScript fixes first two really well and does a half-assed job at fixing the last - channels are too much overhead for such task (from both performance and conceptual POV - putting a queue in front of every callback is silly in a single threaded model).

I feel like Elm is a language designed to explore an idea not to be practical, this is why I haven't tried using it for anything serious.

TypeScript has potential in theory to bring ES6 + type safety to avoid semantic pitfalls - but in practice it's supposedly biggest strength of leveraging the existing JS ecosystem is also it's greatest weakness in that you're still left there with shitty JS ecosystem with zero consistency, most libraries deal with JS pitfalls in their own unique way which doesn't mesh well with other approaches or with TS, fragmented build, packing, dependency management, module loading tools - all trying to solve the same problem in different and incompatible ways - it's just a pile of junk that wastes more time trying to reuse stuff than it takes to build anything - which is probably why most of it is in the state it is - and very few things are actually maintained so the argument that reinventing something means you need to maintain it yourself is not really convincing.

Dart has a built in package manager, has had tools for dealing with async code for a while now (both async/await and streams), it's familiar to the Java/C# devs but it's surprisingly concise (quite a few nice touches like constructor property-parameter mapping or => getter syntax for tedious stuff), the standard library is great and avoids all the JS pitfalls.

The downside is that you still need JS interop because there's no way around it (for eg. emscripten) - and talking to JS from Dart is still nasty - much more involved than CLJS or TS.

Having used all 3 mentioned for non trivial work I like Dart the most and the new JS interop could make it even better.

My impression of Elm was that it's trying to create a platform for real-time interaction (e.g. games, UIs, etc.) that happens to target the browser. There's a constant undercurrent of practicality in the community: for example, new language features are regarded suspiciously, and modifications to existing behaviour are only made when they reduce the burden of complexity to the programmer. Obviously it's a language that has yet to hit 1.0 so there are occasionally large swings in its design, but apparently they've become more stable as of late (I'm only picked it up ~1 month ago).

The only unpractical part I'd noticed was its perspective on reusing components, as seen in this overview under 'Nesting' [1]. That bit of explicit routing aside, may I ask what dissuaded you from using the language seriously?

[1]: https://gist.github.com/evancz/2b2ba366cae1887fe621#nesting