(The comment about the wife spoiling him to use java seemed more credible)
https://mobile.twitter.com/ID_AA_Carmack/status/807797812700...
A former employer was led by huge Clojure fans, who requested that the backend be Clojure, and highly encouraged me to use ClojureScript and something like Reagent for the front-end, which I recently noticed that they finished after my departure. But the more I think about it, the more I can't think of a single feature of ClojureScript that's inherently better than JavaScript, whereas s-expressions are inherently hostile and also have no real-world benefits. JavaScript has destructuring, closures, a flexible module system, object/array spread, basically every syntactical feature Clojure has, but without the s-expressions.
The commonly touted benefit of s-expressions is homoiconicity (code is data), which lets the developer customize the language via macros without needing to wait on the compiler/interpreter authors. But we have this exact same benefit in JavaScript with compilers like Babel. Maybe it's pushed onto another layer of development, but it's in no way less legitimate or useful. The slight benefit you get by being able to do this transformation at runtime is negated by the inconvenience of s-expressions. (Although at first glance, it seems like we could use a better Babel API that lets us transform our code more conveniently. But I think they're working on that?)
The main other benefit I commonly see from people using Clojure or ClojureScript is live development. I saw technomancy made some blog posts about how he used Fennel (a Lisp that compiles to Lua) to do live-coding stuff in Love2d. I was impressed, until I realized that none of these features were specific to Lisp. So I borrowed the same underlying concept he utilized, and started to make a really cool Love2d live development thing just like he had, but in pure Lua.
Now I have the ability to press Cmd-E in VS Code while editing some Lua file, and if I have any code selected, it'll be sent to the live Lovd2d game that's running, to be eval'd, and if there's no selection, the whole current file will be sent. It's the coolest setup ever, almost identical to the awesome setup I had with CIDER + Clojure + Emacs, but without Emacs or s-expressions.
Most of projects wouldn’t even consider writing a loader for a compiler or using a syntax parser. Yet to add which method is more prone to errors.
(And, of course, meta-programming is powerful, thus 'dangerous', but there's a lot of things that are only really feasible with it.)
I used to write C and C++ code in (non-vim) vi, with only autoindent, and it was fine. I could also type the spaces, if I had to.
For s-expressions, like in a Lisp, you want autoindent a bit more, and some kind of of paren-matching/highlighting is also important.
Fortunately, editors have been doing autoindent and paren-matching for decades. And once your editor is formatting your code correctly, it's pretty visual, not textual.
And few people write any code in something like non-vim vi or notepad.exe anymore, so I see no reason to think less of a language for wanting an editor/IDE developed in the last couple decades.
There's no need for paredit, AFAIK, but paredit is another thing for which s-expressions are especially well-suited, relative to most other syntaxes. (You can also do paredit-like structural-based navigation and editing of JS, for example, but you have to have a clear model of the syntax structure, not only in the tool, but also in the programmer's head.)
Just not on UNIX culture.
Interlisp-D, Lisp Machines, Smalltalk, Mesa/Cedar, XDE, Oberon were an OS as IDE.
UNIX is a poor imitation of their capabilities.
Oberon is interesting because the 2013 book goes much further than any recent system in this regard. One of the (final?) chapters even describes how to implement the custom processor in an FPGA.
I concede that I do not do any web or graphics development at the moment, this might help. But it's deeply satisfying to be able to discover new helpful functionality in the tools that are there for decades. And it's good to be able to use them almost everywhere, be it on a server over ssh or on a windows box with cygwin.
Culture matters, which is why I should probably learn Rust...
Then, one day, I turned it off. I'm not sure why.
I think it's because I didn't want to get too attached to it. I reasoned that Paredit wouldn't always be in the places I was working with Lisp, so I had better remain flexible enough to not need it. And between the editor's paren matching and the way I format my Lisp code, I can be pretty productive in Lisp without Paredit or a similar structural editing tool.
Also, AFAIK Paredit is pure Elisp, so should work just fine in Windows.
I enjoyed Lua's simplicity, portability and execution speed. It shares much of JS strengths, with fewer quirks. I was productive after a weekend of reading Programming in Lua book.
Clojure took some more time because I had to unlearn some habits. It's for the best, I think. It's not just about homoiconicity and REPL. In Clojure it's hard to do the wrong thing, compared to JS or Lua. The language makes it easy to isolate a small piece of functionality, develop and test it, and then and put it together into coherent whole. Clojure may be harder to learn, but the cognitive load is smaller and it scales better for large projects.
Persistent data structures are also huge step forward. They eliminate whole class of errors and allow for traceability and reproducibility.
As for s-expressions, yes, they can be a nuisance in editor without proper support. I'm getting by with SublimeText + zprint auto-formater. For REPL I found no better than Atom's proto-repl. Also I hated Lua's "if..then..else..end" and other verbose constructs. I'd say s-expressions are more consistent and compose better. Still, your frustration is familiar to me.
I've seen and even written a lot of really really awful Clojure code which technically fits the criteria for good code, on paper, but completely misses the bigger picture. (People have made the same argument for Go, that it's idiot-proof, but I've seen plenty of awful Go code too.) I'm more and more convinced that bad programmers will write bad code in any language and good programmers will write good code in any language.
BTW, for Sublime, you could install the Terminus package to get an integrated terminal to run clj(1) in. Then with a few lines of Python, you could write a command to send text from the editor side to the REPL side.
(I have a half-baked enhanced Clojure package with some features in this vein, but it's not public atm)
Clojure also interacts with Java, but it feels like CLojure is the host that runs Java methods as needed. The separation is clean and you know exactly when you are crossing the boundary into stateful territory.
I see the value of those languages for lispers wanting to script the platforms where Lua can run, but not really as a core language for modelling your domain.
I'm still learning about Scheme/Lisp, but doesn't hot-loading have to do with the size of "compilation units?" Isn't a single s-expression treated as a compilation unit whereas in a language like Java, the compilation unit is an entire Class?
Rich Hickey touched on this in a comment here on HN (2nd paragraph): https://news.ycombinator.com/item?id=2467809
Persistent data structures, multi arity functions, multi methods, transducers, data oriented design, spec, repl driven, better live reloading, meta data, protocols, core async go channels, atoms, Datoms
There are breakthroughs in Clojure/Script that literally do not exist in any other language, let alone JS
Datomic, Fulcro, Crux, garden, hiccup, honeySQL, rebl etc just straight up don't have equivalents in other languages
Your statement blows my mind, sure you don't need s-expressions to do most the above but you can just turn on parinfer and treat it like python whilst still being able to dynamically rewrite source code using data manipulation
Multi arity functions: That's actually a minus, not a plus. The vast majority of the usages of this are solved with options params with default values in a much clearer way.
Multi methods: In 5 years of using Clojure professionally, I think I've never seen these used once.
Transducers: This is just an optimization, and I'm pretty sure it's possible in JS too since it's an API design.
Data oriented design: This is a design practice, it can be done in JS too, and it's a questionable one too.
Spec: When Spec came out is around the time I realized the period of true Clojure innovation is over, which peaked from around 1.3 to 1.5 or so. TypeScript is by far a better version of adding type innovations to a dynamic language.
REPL driven: You can do this in any language with a REPL, even JS.
Better live reloading: This is just as good in JS. There's no language feature of ClojureScript that makes this better than in plain JS.
Meta data: The only times I've ever seen this feature used was in storing internal representations of things in DSLs, which can be done in JS by using plain objects for their representations which store an inner object.
Protocols: JS only doesn't have this because it's duck-typed and they are implicit. An explicit counterpart exists in TypeScript's interfaces for those who want it to be explicit.
Core async, Go channels: These have to do with threading, but JS mostly exists in single-threaded runtimes, so it's non-applicable.
Atoms, Datoms: These are library-level concerns which are doable in JS.
1. Parentheses are inconvenient.
2. Everything you can do in Lisp you can do in $other_turing_complete_language.
What you're missing in (1) is that as much as you wish you were stating an objective truth of the universe, you're really just stating your own subjective impression. If parentheses were objectively, universally inconvenient, nobody would ever program in Lisp. And yet many do. So what's the explanation for that?
What you're missing in (2) is that although (2) is certainly true, it's irrelevant. If (2) were relevant, nobody would ever prefer any Turing-equivalent language over another. And yet objectively, people certainly do. So what's the explanation for that?
The other thing you're missing in (2) is Greenspun's tenth rule, but I'll let you look that up.
Why, they are impractical buffoons who lack the benefit of my own vast personal experience, which trumps everything. Or else blind zealots.
When all these things are baked into your language, you're free from all the worries and cognitive load to make sure everything works as you expect it to work.
As an example, my team has been using Reagent on the front-end for around 5 years now. We've never had to go back and change code to accommodate changes in ClojureScript or Reagent in that time. And we don't have to deal with stuff like JSX because HTML can be expressed using regular data structures. Things just work. Meanwhile, React itself has had many changes, and some of those are breaking.
I also find that tooling for ClojureScript is strictly superior. You get REPL driven development, reliable hot loading, minification, code pruning, and code splitting out of the box. All these things are difficult to do with Js, and don't work reliably with many NPM modules.
1. https://yogthos.net/posts/2013-08-18-Why-I-m-Productive-in-C...
It's also possible to avoid JSX completely, thanks to a library that uses JavaScript's template strings feature: https://www.npmjs.com/package/htm
> No matter how Racket evolves, the community is committed
> to preserving what we have achieved: today's `#lang racket`
> programs will run in the future, and today's
> `#lang racket` modules can be used in future Racket programs.
But really there are lots of other, better reasons to use JS over Racket for a major commercial project, which may or may not be actually a good call, but a new proposed syntax is not one of them.