I say this after running the last command in the REPL - it's what I've been doing for the last 2 hours. I haven't looked at the browser since I started.
I switch to the browser and sure enough, the whole thing works perfectly.
This is the Clojure(script) experience - it's a different method of developing applications. Combined with the brilliant weirdness of the language itself, it sucks you into the REPL and persists even after you've finished coding.
The most suitable word for this experience is "hacking". You don't code your program, you hack it.
The only drawback of Clojure/Clojurescript is the high learning curve.
Maybe the 20 years of OO programming have really left a dent on my brain or maybe it was because of the young age of the ecosystem, or maybe because tooling wasn't quite there yet, but I found it quite a challenge, before I could write even basic programs.
Even now, after having spent months learning it, I still find it intimidating.
Oh and did I mention how much clearer it is to write C++ after learning Clojure ?
Pure functions, immutable data, data vs code.. just some of the concepts that help a lot when you bring them to an OO language.
I'm a recent clojure convert too (haven't used clojurescript much).. I have to say, the only thing that annoys me about it occasionally is its marriage to the JVM. It took a few months to get into it from a C++/Python/Go background, but now, hacking in other languages (maybe less so in Python) feels like sculpting while wearing oven mitts - needlessly tedious and overwrought.
But, to make a unit test by coding the function, making a lovely structured literal of the input data, running the function in the repl, and then pretty printing the hand-verified output to paste in as the expected test result is just the least effort I can imagine for that kind of coding process. I'd count myself as happier if every language had as strong a system for printing and reading in data literals.
It seems that the JVM cannot be blamed for the slow startup time when using Clojure, but compiling Clojure's own core library is what makes it slow. And if you begin to add dependencies it will become even slower. The itself JVM is able to start quite quickly.
> but compiling Clojure's own core library is what makes it slow
Why would it do that every time one compiles one's project? Surely Clojure must keep a cached linkable byte-code/binary somewhere per version/OS/arch, as all modern languages seem to do?
Startup times are unpleasant, but my main reservation about the JVM is oracle.
I invested a lot of time in learning clojure, but Oracle concerns me enough that I'd rather not build my knowledge stack on them.
So I've stopped using clojure. I may look into clojure script again in the future, but it still feels like a kludgey experience to me. Inspite of the amazing efforts by David Nolan and the community.
You should work in PR. I mean that as a compliment. You've managed to give a clear and intuitive understanding on exactly how it feels to write clojure, and it sounds really nice...
So with respect to the high learning-curve, a few questions:
1. Were there any non-obvious concepts that you wish you'd understood going in? i.e. anything I can do to make my learning-curve shallower than yours?
2. Can you recommend any resources for getting started?
> I haven't looked at the browser since I started.
> I switch to the browser and sure enough, the whole thing works perfectly.
This is the general workflow in a typed language as well (i.e. PureScript, Elm) - you write some code and then fix the type errors in the editor - only that instead of running a couple REPL queries and just assuming that your app works, you really do have some hard guarantees in all edge cases, like no crashes, no type mismatches, logical coherency.
Most of the time this then means that the "app works".
I love the JVM repl experience from Cursive and emacs. But I've never come close to it from ClojureScript for browser development. Can you describe your repl capabilities and dump some links? (I am looking for a real repl experience like on JVM where the IDE is hooked into the repl and forms can be sent across individually and the app can be built up piecemeal without restarts. If this isn't what I want, tell me what I want. Figwheel doesn't seem to be it.)
In Emacs, you can use CIDER together with ClojureScript. By following these instructions[0] I was able to get the combined Figwheel+REPL functionality.
I wholeheartedly agree it's easier to write clearer OO code after spending time writing in Clojure.
But I think another drawback of cljs is readability. It's super cool that everything is data and data structures, but it makes it harder to eyeball code, just takes longer to parse and understand how these data transformations actually map to side effects on the page.
IMHO well written OO code is easier to understand and reason about.
Very nicely written, I have to say it sounds very similar to my experience using redux/react. Although there are some things like homoiconocity you don't get writing functional JavaScript I do feel the learning curve is much less steep than ClojureScript.
You don't get code out of the REPL. You evaluate your code inside the REPL, often directly from the source files.
For example I'll often write a function in a source file, evaluate it from there so it exists in the process, then call it from the REPL to test it works correctly.
When I launch the program, every temporaries made at the REPL are gone, but the source stays.
That is the workflow I currently use, too. Luckily, Leiningen reloads most of my changes. I wished, though, there was the ability to save the current name space from the REPL. Hacking together a couple of functions and save their definitions when you're happy.
That's inverted I think. For JVM clojure I am always editing inside a .clj file, all the scrap goes in (comment) forms in the file and I send forms to the repl from the file. I rarely actually type in the repl.
In a number of languages with a number of editors / editor plugins, with a code file open I can easily keep open in parallel a REPL window/tab that has the code file (and libs/pkgs/modules that it imports) loaded in interpreted mode and can reload either on save or if that isn't hooked up, certainly by a repl command like `:reload` in GHCi which I use in SublimeHaskell. Marvellous "live-coding" experience of sorts. Works particular well when writing pure functions (same in = same out, every time, no side-effects etc) and in a language where quickly defining some temp vars for passing some random test arguments in a swift non-verbose manner is a total non-issue
There are many editor plugins/configs which will allow you to evaluate code from inside your editor.
For instance, I use tmux/Vim/Vim Slime, which allows me to send code from the editor to the REPL (running in a separate tmux pane). This setup isn't as embedded or as fancy as some of the other options (e.g. Light Table). However, this setup doesn't force me to break my standard workflow and works with other languages which offer a repl or interactive console (e.g. Scheme/Racket, Ruby, Haskell, JS, etc.).
This article provides zero data to back up its claim that ClojureScript is the most-used functional compile-to-JS language, just a list of companies that use it.
Crude, but combine companies using with Github stars and I'd say the claim is accurate.
Second most used FP <> JS language would probably be Scala.js, which, as far as we know[1] doesn't yet have many companies publicly using it.
Take it all with a grain of salt, OP himself says, "ClojureScript has almost certainly become the industry's most-used functional language that compiles to JavaScript. Admittedly, this is like being the most popular person who can speak Klingon".
Translation: combined adoption of niche languages <> JS is still miniscule.
Elm in itself is useless; the claim here is that among FP server-side languages that compile to js, CS leads the pack.
Sure, with Node.js on the server then Elm, Purescript, BuckleScript, etc. can do client-server, but they can't do so in the same language, seamlessly share code, etc. That's one of the selling points of one-language-to-rule-them-all (i.e. target js, wasm, native, clr, jvm, ...).
Saying that, looking at BuckleScript this weekend -- completely shocked that there's zero overhead wrt to generated file size; it's like typed Coffeescript a la OCaml, unbelievable (this from a dev using Scala/Scala.js in day job).
The author must have a very particular idea what "functional" means to include Clojure but not JS. That's fine, though it'd be enlightening if he shared it.
My personal pain point with JS as a functional language is its lack of native persistent data structures and the fact that its mutable-by-default nature has spawned an ecosystem of libraries and frameworks that overwhelmingly exhibit reckless disregard for managing mutable state in a sane manner.
The lack of native persistent data structures means functional programs need to depend on libraries like ImmutableJS and Mori to fill in the gap, but in doing so they have to trade away language features like spread and destructuring that improve readability and minimize boilerplate, and take a hit to ease of debugging. I almost always pick up ImmutableJS or Mori for my JS projects these days despite these issues, so it's certainly not the end of the world, but whenever I work with them it's painfully clear that persistent data structures, and by extension functional programming, is a second class citizen in the context of the language.
The ecosystem issue is a much harder one to work around however. You can take every precaution to carefully craft your code to minimize side effects and manage state transitions, but there's only so much you can do if your components/visualization/networking library (mis)manages its own internal state. The situation has improved dramatically in the past few years with the advent of React/Redux, and we've experienced a functional programming renaissance with tons of great new libraries popping up that are amenable to functional paradigms, but we're only just starting to make a dent in the massive JS ecosystem, which is still overwhelmingly inadequate when it comes to dealing with mutable state and managing side effects.
All of these pain points become painfully obvious once you've worked with an ecosystem that has spawned from a functional-first language that is immutable-by-default and encourages being mindful of state and side effects (in my case, that language was Clojure and ClojureScript, but that's certainly not the only one).
You must have a very particular idea of what "functional" means to include JS. What definition of functional programming does JS meet that you can't apply to nearly every language? It can't be that it just has first class functions, lambdas, or higher-order functions: many, many languages have that.
Functional languages emphasize immutability. Javascript does not do this. Clojure[script] does.
> What definition of functional programming does JS meet that you can't apply to nearly every language?
Since functional programming is a programming paradigm, not a language, don't be surprised if many languages support it, just as many diverse languages support OO.
The language support generally thought necessary is first-class functions (and consequently concepts like higher-order functions). For example, Java < 8 and C# < 2.0 aren't usually considered functional.
If you think allowed mutability makes or breaks a functional language, then neither Clojure nor Clojurescript fit the definition.
* first class functions that you can pass around
* anonymous functions
* closures over functions
you can get by. This is what enables you to create the usual higher-order function toolbox, all those nice functions that convert your code to long list of composable map/filter/reduce/zip/walk e.t.c.
I remember reading that python got here iteratively, i.e. you could pass around functions, but they needed to be defined top-level, then you had anonymous functions, but you needed to pass in variables from enclosing scope explicitly, and finally you had closures.
But I agree that fast, immutable datastructures make all of this much nicer. But not having them is not as much of a deal-breaker.
Similarily tail-call optimization. I used clojure at work while dealing with haskell on a school projects, and from that time on, I found "loop/recur" kinda ugly :-) NodeJS with --harmony actually supports it now.
Similarly, pattern matching. Don't you need to pull that as a lib in clojure as well? With destructuring this almost is a js feature.
It's functional. It has all the components needed to code in a functional style, but at the same time, lets you use other paradigms, such as OO, when they make the most sense to do so.
I'd argue a "pure" functional language is akin to trying to write a novel on a keyboard without an "e" key. Sure, you'll eventually stumble and write "Gadsby", but there are times when being able to cheat is useful in making your code more clear and concise. When I code stuff in C++ or C#, I keep my objects immutable most of the time because it's the easiest way to rationalize the various components that way, but at the same time, when I'm doing things like handling I/O, a pure functional style gets in the way.
For sufficiently complex & performant functional code you need to have immutable data structures. Clojurescript has this built into the core data types by default, in Javascript you need to use an immutable type library, but then you have to do a lot of awkward data marshalling into the immutable types that really hampers the experience.
This might be off topic but why does functional code specifically need immutability? I'm familiar with the benefits of immutability in general (actually one of the best code bases I've ever seen was written in Java with mostly immutable object) but I'm not clear about the specifics of functional programming that makes it fit particularly well
A function has the structure INPUT -> FUNCTION -> OUTPUT. Let's say you're adding a widget to the database: (DATABASE,NEW_WIDGET) -> FUNCTION -> (NEW_DATABASE)
The only efficient way to return a brand new database that is the same as the old database plus 1 new widget is via immutable data structures.
Calling JS a functional language is like calling a tricycle a sports car. Apart from first class functions it has nothing any decent fp language has, like: partial application, currying, immutable data structures (it does not even have decent non-immutable ones), etc. You can work around some of them (albeit painfully) but why would you do so if you can use a real fp language?
> no immutable data structures (it does not even have decent non-immutable ones)
I'm no newbie to FP (IMHO) but this has me perplexed now.. is there a difference between mutable and "non-immutable" ones, or do you mean to say that JS has neither immutable nor mutables ones? ;)
I'm learning clj/cljs .. finding that experience a blast! I almost never do any front end work, but with cljs and re-agent, I found myself productive. Clj itself strikes me as a very well thought out language
I enjoy clojure a lot, I've had some difficulty getting started with cljs though. It seems like all the guides on getting set up with tooling are out of date. I'd particularly like to use it with node for scripting tasks, since the Clojure startup time makes it impractical to use for anything that isn't a long-running task.
Lumo[1] probably fits your use case perfectly. It's a ClojureScript REPL running in a NodeJS context. You have access to both bootstrap-ClojureScript-compatible libs as well as the whole NodeJS ecosystem. The REPL has some nice ergonomic features and comes with a TCP socket REPL you can use with Emacs inf-clojure and (I hear) Cursive.
Also, there are a bunch of languages with functional features which compile to JavaScript. I don't think ClojureScript is more popular than, say, TypeScript or Dart.
If I'm on an engineering team that's using Dart or TypeScript I'm not going to lean on functional programming patterns the way I would if I was on a team using ClojureScript. Idioms matter when you're working on production stuff with other people.
But sure, TypeScript appears to dwarf all other compile-to-JS languages in terms of actual usage.
I think Clojure or ClojureScript is a very important as a member of the Lisp family and yet also compiles to Java or JavaScript. This is complementary to languages like Elm or Haskell which emphasize type purity. I have gone the second route, but many people dislike it.
I once ran a meetup with ClojureScript Koans and people loved it. I should hopefully add a few.
I dislike all compile-to-JavaScript languages. The compilation step is a massive overhead and it becomes a major pain in the neck once your project reaches a certain size.
Whatever shortcomings of JavaScript these languages claim to solve are not worth the pain and suffering that this extra compilation step adds.
They all claim to compile really fast, yet whenever I have worked on a project which uses a compile-to-js language, they always seem to end up taking 10 to 30 seconds to compile each time and this slows down debugging significantly.
The beauty of ClojureScript is that you don't have to recompile and reload your page all the time. Development is done using Figwheel https://github.com/bhauman/lein-figwheel and things reload live in the browser as you change the core in the editor.
Not only don't you need to wait for recompilation, you don't even have to reload the page to see the changes. Everything happens interactively as you're writing the code.
I don't think this should be downvoted, it's a valid perspective. Compile times matter.
I use typescript and have it watching my files. Compilation (or transpilation, whatever you want to call it) time isn't an issue. I could see it being quite a hindrance if I had to run tsc on my entire project every time I changed one line, I don't know if this is the case with clojurescript, but it would be a downside for sure.
Most compile-to-Javascript systems I've used have a "watch" mode which recompiles every time any source file changes. The initial build of a large project may take 30+ seconds, but after that, a rebuild when a source file changes takes a few seconds at most. And with some systems, you can even do live edits that apply immediately to running code, so you don't even have to refresh your browser, making development actually faster than not using any build process. (Good examples of this are hot module replacement for Browserify[1] and Webpack, and Figwheel for Clojurescript.)
I'm a huge fan of speedy compiles. If you don't get sub-second compile times with BuckleScript (OCaml -> ES5), come shout at the #bucklescript channel in https://discord.gg/reasonml :-)
While you are right that the pain of the compilation step is immense, I think you may not appreciate all the niceties we have in Clojurescript land that you have to do without in javascript land.
> they always seem to end up taking 10 to 30 seconds to compile each time
Oh yeah, that's huge, at least the time to sip half a glass, that's scandalous. Hmm... you're kidding, right? How can it be a problem in absolute value, and furthermore how can it be a problem in relative value? That time should be absolutely negligible vs the design, the thinking, the typing, the thinking again, etc.
> and this slows down debugging significantly.
There is also the option to think about what you program instead of proceeding by trial and error.
I don't want "a project of a certain size" to be handled through trial and error. And yet, according to the pitiful results and condition of current software, especially shitty web apps, that has to be representative of the current model of development.
I doubt you have done this kind of development. The advantages of fast edit/compile/run cycles are widely acknowledged (as are the advantages of compiled languages). Many development domains profit greatly from instant feedback.
"Should I put more space here? Is the border too thick? Let's see".
"Oops! I know why it's failing. I'm quickly going to fix the order of the arguments".
It's great if you can do these things in 3-10 seconds instead of minutes. Not everybody is designing airplane control software.
I maintain a small ClojureScript library, but I really don't think this article is missing the point. The people comparing ClojureScript, Scala.js, Elm, PureScript, and GHCJS are looking for something pretty specific. Not just persistent data structures and the same map/filter primitives that are now available in Java, but expressive and strong types. ClojureScript rejects that this is even something you'd want.
Most people I know who actually tried Clojurescript like it and would like to stay within its proper. Why don't you try it? I myself after trying Coffeescript, Typescript, Livescript, Gorillascript, Fay, Haste, GHCJS, Traceur and Babel found Clojurescript and it brings me an immense joy. Every single day. I'm not even mentioning lots of templating engines, CSS preprocessors and bunch of js testing frameworks, bundlers and build tools that I had to deal with. That pain is gone. The work become a joy.
I say this after running the last command in the REPL - it's what I've been doing for the last 2 hours. I haven't looked at the browser since I started.
I switch to the browser and sure enough, the whole thing works perfectly.
This is the Clojure(script) experience - it's a different method of developing applications. Combined with the brilliant weirdness of the language itself, it sucks you into the REPL and persists even after you've finished coding.
The most suitable word for this experience is "hacking". You don't code your program, you hack it.
The only drawback of Clojure/Clojurescript is the high learning curve.
Maybe the 20 years of OO programming have really left a dent on my brain or maybe it was because of the young age of the ecosystem, or maybe because tooling wasn't quite there yet, but I found it quite a challenge, before I could write even basic programs.
Even now, after having spent months learning it, I still find it intimidating.
Oh and did I mention how much clearer it is to write C++ after learning Clojure ?
Pure functions, immutable data, data vs code.. just some of the concepts that help a lot when you bring them to an OO language.