I do Clojure and Clojurescript full time. Love the ideas, but we as a community need to step up our game when it comes to how easy and fun it is to get started. Setting up a Clojurescript project is even worse than "modern javascript".
The only worthwhile way is using Martin Klepsch's "Tenzing" template, but using a template is not a solution.
I worry about this since the power of Lisp and Clojure is how easy it is to experiment. Starting a C project is just doing a basic Makefile, a javascript project is just `npm init`.
I appreciate Racket's huge emphasis on making it easy to get started. Not relying on complicated tooling (but making it possible for advanced users to do so) makes Racket an incredible way to begin learning Lisp.
Follow along, hacker news comment reader! This only takes a second!
No need to putz with the command line (but you can $ racket that-file.rkt if you want to). No need to fight with Homebrew and Racket's package manager (but there is a package manager if you want it). No need to install support for your own text editor (but if you don't want to use DrRacket, you are more than welcome to Emacs-Major-Mode it up if you want).
Racket does it so well! I presume Clojure can't, because it is for good and bad tied to the JVM platform, and has to be bothered with classpaths, classes & methods and what I call "the java mindset" of crazy XML-heavy mazes.
I've tried so many times to get started with Clojure and Clojurescript, and when I'm trying to set up the environment from scratch (because to learn it I have to understand how all pieces fit together) I just get hopelessly lost. Broken deps, files to edit to make the Emacs REPL work correctly, snippets of code I have to add here and there to create a simple project.
And last time I checked, now there's this Boot tool to learn, to choose and to understand (and to debug its stacktraces) instead of Leiningein -- ARGH.
I grok a fair share of languages but there's nothing like Clojure, ClojureScript and Javascript to make me feel like an _idiot_. I don't like that feeling.
For the love of $DEITY, language designers, learn from Go: they have an up-to-date spec, an up-to-date "Effective $LANG" document and one (1) simple, official and maintained build tool. Otherwise you're just, as we'd say in Italy, brushing dolls.
I made it to show how easy it is to get started with clj / cljs web development. It uses closp, which covers everyhting from configuration to front / backend work, migration, tests, selenium tests, template generation and so on.
Also have a look at the intro: http://closp.net/pages/closp-intro/ which shows how you can get started with 5 commands in the console (given you have leiningen / jdk installed, which would be two more steps, if not).
There is also the possibility to run lein uberjar from the project and get a standalone jar with webserver and compiled clojurescript generated.
Then there is luminus which is somewhat similar and provides much of the same featureset and has a great documentation: http://luminusweb.net
Regarding boot I agree with you, its confusing to have two build tools. It adds some features that leiningen does not provide and for which there was a need. That said, the number one build tool is still leiningen.
Stacktraces are aweful, agreed too, there is no satisfying solution to it that I know.
Comparing C and Makefiles and `npm init` just doesn't make any sense to me. For something in style of the former (i.e. no real dependency management) you have the ClojureScript Quick Start - download a JAR & make a build script, done. For the later I just don't see how `npm init` is any easier / different than `lein new mies easy`? And doing anything serious with Node requires reading up on a far more dizzying array of choices than the Clojure(Script) ecosystem offers.
This. I've trimmed down severely on my use of Clojure(Script) because setting things up for kicking off small projects is just too time consuming, and large projects I dip into use other languages.
Where it regards ClojureScript specifically, doing a simple MVP/PoC web project or a Cordova app is just way faster to get going in vanilla JavaScript.
As to Clojure, every time I have to invoke lein it feels very much like this:
It seems to me that setting up clojurescript project is extremely easy. Basically you need to know only one "build" system(leiningen/boot), as for javascript you would have to know at least few(broccoli/npm/bower/gulp etc). And with leiningen you can do `lein new` as well, although I prefer boot and creating my own files:
The most complicated thing here is probably the bash quoting/escaping (which explains the double backslash inside printf()'s arguments).
The above command line spawns two processes, one of which is a full C compiler, and it runs in 3 milliseconds on my embarassingly old home laptop (Core-I5 M480 at 2.7 GHz).
I'm not trying to be the old fart claiming that everything was better back in the day, but this (to me) almost makes C seem as approachable as BASIC on the old 8-bit machines, when looking at the relative complexity.
I think comparing the compilation of a C hello world program to a web-project, with inbuilt test scaffolding, serving of generated content, communication across a network and safe multi-threading of the network stack is a little bit of a mismatch. If you build a C project with all of that in it, I think that it would be a bit more than a 1 liner, and it would take a little while longer to compile. Sure not as long as the clojure project to compile but not 3 milliseconds.
Boot is great. I'm not an idiot developer, and have accomplished quite a few things with code that I'm proud of. But please don't ask me how to setup nrepl with boot+cljsbuild and have the repl run in a browser or on node.
I am heavily thinking of implementing a language that is inspired by Clojure, but native to the JS land. It would use npm, Mori for data structures and browserify (if not on Node.js) under the hood for requiring libraries. Basically a transpiler from Lisp style code to JS, with better data structures.
That sounds like it would be an awesome language! I love cljs but often share the feeling that its implementation is too heavy and too far removed from JS.
But then you at least want Figwheel in there, and it's not necessarily easy to know how vastly better your life will be with it if you've never done cljs before. It would be neato with a cljs IDE for approachability. An MVP could be lein + cljs + figwheel + lein ancient, editor + webkit viewer.
I'm late to the party but since the poster only showed us the tree we don't know what else is going on in that build.boot.
boot-http, boot-cljs, boot-cljs-repl, and boot-reload
Provide all the features of lein-ring, lein-figwheel, and lein-cljsbuild with a simpler setup and no clojure neccessary in a clojurescript project.
Serving compiled clojurescript, reloading cljs and css, with a browser repl, is really simple in boot. The boot tools work together better than the leiningen tools do.
If you look at the Tenzing template's build.boot, which doesn't have a lot going on, you'll see how straightforward the meat of it is, without ending up with other JVMs.
lein new tenzing some-project
emacs some-project/build.boot
M-x cider-jack-in
// switch to repl:
(boot (dev))
M-x cider-replicate-connection
// switch to new repl
(start-repl)
That's it. The first repl gives you feedback on the dev server and reloading. The second repl gives you a browser repl. Cider has also levelled up, such that when the browser repl connects it gets picked up by nrepl and just works. All of this with one JVM... and no Clojure sources in your ClojureScript only application.
I stress again that the template really doesn't do a heck of a lot and the build.boot, the filesystem layout, and the way boot works is really the whole of the story. I wish Boot were better documented/marketted, so I could've switched to it long ago.
I think you're missing the point if you want this. Boot once, leave running, swap in new code, interact. A clj process (like most lisps) is alive and dynamic in more ways than you might be used to. There's no need to be constantly booting it.
Alright, how about running it on Node then? I assume this is mostly for scripting, as long running servers and the like wouldn't need to be concerned with start-up times.
That might be a solution, yes. But Node is a single-threaded engine, so I'm not sure if I can run parts of Clojure which use multiple threads, or if there are other restrictions.
Anything that uses the JVM will never (at least for a long time) achieve the startup time of cpython, cruby or node, less of ls and grep, so don't get your hopes up :) - that said, most of clojure's startup problems lie in clojure itself and not the JVM.
Depending on your definition of experiment I think Clojure has a MUCH better story for getting to a good beginner setup with live reloading than webpack/react
It's interesting to observe how almost all mainstream dynamic languages move from "who needs types? types are very very bad and just get in the way!" to some sort of gradual typing or runtime type validation.
* Javascript has Flow/TypeScript
* Python has PEP 484
* Perl6 has some sort of types
* Clojure/Script has core.typed, schema and now clojure.spec
* Don't know about Ruby, but I've seen articles on type checking/annotations
* Erland has Dialyzer (although this is not a recent development)
I have a bit of a theory on that, which I bet will be a bit unpopular but is based on some social things I have seen. A dynamic language gets popular[1]. As more people show up, they want to bring their way of doing things (e.g. "prototypes are stupid, we need classes") instead of fully adopting the language. Improvements are cool but some of the changes alter things in a fundamental way (e.g. dynamic to static). Sometimes it leads to second systems (e.g. Objective-C to Swift) and is often accompanied by "the need to grow up". Then we get a new language.
1) could actually be any language that doesn't fit the mainstream mold
Which is why what Rich Hickey did with clojure.spec is so important...
He acknowledged the deficiency, but worked through it to understand what the problem actually is. Then he created a solution that solves the real problem, in a way that avoids diminishing the character of the language.
Have a read through the rationale on the design of clojure.spec to see what I mean.
Common Lisp has (always had) a rich type system. You hardly pay any attention to it at all when you're prototyping but once your code has taken shape you can start adding type annotations and then it can really fly.
So yes, types do get in the way while prototyping but it's great to have them available when refining code.
Are Common Lisp's type annotations for consistency checking or just to improve performance? Haskell and ML have types, Pascal and C also have types, but they serve very different purposes.
And types can also help you prototype and then polish rough ideas. For example, I'm exploring a programming style where no unreachable code paths are allowed: in a circumstance where you'd throw an exception or return an error sentinel, I'd refactor the code to eliminate the unreachable branch. This means that abstractions must have the “right shape” (that is, not leak), and types are fundamental to make this work.
When I start adding type annotations, I'm usually aiming for a speed-up but more often it leads to reorganization and rationalization of the code. The code becomes more streamlined and beautiful.
The post below resonates with me. He calls it 'programming by teaching', where you gradually shape a program to solve your problem. I think of it as 'programming by learning', where the Lisp way of developing helps me learn about my problem. It's only when you have the final version of your code that you fully understand the problem by seeing its solution in front of you.
What I do can also be called “programming by learning”, but it has a fundamentally different flavor than what Lispers do. I present my ideas to the Poly/ML REPL as a hopefully coherent argument, and it tells me whether, at the very least, the argument is internally consistent. ML doesn't “know” about my specific problem domain, but it does know logic, and it's very good at nit-picking, which makes up for my human inability to exhaustively pay attention to an ever-growing number of tricky details. Furthermore, ML has all of nominal, structural and abstract types, which lets me “steer the reasoning” in the desired direction:
(0) Nominal types correspond to primitive domain entities.
(1) Structural types correspond to compound entities, built from simpler ones.
(2) Abstract types regulate the interaction between different subdomains of a larger domain.
The net result is that the REPL's feedback helps me identify those parts of the problem domain where my understanding is weak. Not only the REPL's feedback, of course. For instance, if my program contains unreachable code paths, it means that I haven't yet found the right abstractions.
The philosophy behind this methodology is that:
(0) The cost of having a wrong program is actually larger than that of having no program, because it takes too much effort to identify the wrong parts so that one can fix them.
(1) When the size a program exceeds a certain threshold, it becomes impossible for ordinary human reasoning to determine with full certainty that a program contains no errors. Only formal logic can help, and only machines can do formal logic at a large scale.
(2) It isn't practical to tackle large problems without a properly enforced separation of concerns.
---
By the way, ironically, I never make an explicit type annotation, except perhaps, perhaaaps, at module boundaries. Figuring out the boring details of how to connect the pieces of a puzzle is a job for computers (type inference), not humans.
As a language grows in popularity, so does its use cases. There are use cases where static typing adds a lot of value, and use cases where it detracts value. In my opinion, a general purpose language will (and should!) try to cover both use cases. You see the inverse evolution with static languages--a hard-lined static language evolving more dynamic features--for example Java and its dynamic counterparts (Spring, AspectJ, annotations, etc.).
I'm drinking the TypeScript Kool Aid right now and I'm still not convinced. Ill give it a chance, since I'm still early in the project.
The good: it uses a structural type system. For someone who got the good old 'JavaScript is a toy language you should try Java or C#' at university, this feels really refreshing. It mostly works as JS and you get some nice auto complete. So it feels dynamic and not clunky, like the aforementioned 2 'true' languages.
The bad: the tools seem to work random. Sometimes they pick up the definitions, next time you open a file, 'cannot find module 'react'' or numbers in import paths break the auto complete etc. also often you don't find type definitions for you libs and as a type-n00b you often lack the skills to create good ones yourself.
Anyway, I hope with TS 2.0 things will get better. Especially with strict null checks, which hopefully will be a giant win.
The fact that folks in the comments are complaining here about Clojure's error messages here tells me that the implications of this post might not be obvious. Up to this point, it has been really really hard to give good error messages from macros, and a lot of the core functionality of Clojure (let, defn etc etc) are from macros. What Clojure.spec means (if I'm correct) is that in the future, not only will the Clojure language itself be able to have sane error messages, you'll be able to easily create sane error messages for yourself from your own code. This is a HUGE HUGE deal. It is quite possibly BETTER than any type system. http://clojure.org/about/spec Also, the idea that you'll be able to get generative testing FOR FREE, is just totally insane.
I hate to harp on this issue, but I tried Clojure for about a year on and off. I just couldn't get over the really crappy stack traces. I would break something and then spend 10 minutes tracking down whatever I broke. I'm happy with Kotlin for the moment, but would love to get back into Clojure if only it would get sane error messages.
I used to think so when I was new to Clojure. Then I realized that the stack traces just show you the call stack - nothing more, nothing less. I realized the thing that put me off was seeing Java stuff in the stack trace.
But this is the beautiful thing of Clojures hosted nature. Clojure is just a library, that can be dropped into a project. Thinking about it like that made me appreciate the traces.
Clojure is designed to be a practical language. Part of that is interop with the host VM (JVM in Clojure's case). The "crappy" stack traces are definitely NOT crappy when you're using Clojure in a complex JVM ecosystem. The raw-ness and simplicity of it is actually refreshing. The Clojure Java source is remarkably simple, and it doesn't take much time to get used to and appreciate the Java stack traces when writing non-trivial Clojure code in a Java world.
I just don't see how it's remarkably simple. In other langs with good stack traces I get exactly the error at exactly the line on which it occurred. In Clojure, I quite simply don't. I do absolutely love the rest of Clojure. I also realize there are tradeoffs to being hosted. Nevertheless, to deny that the fact that the stack traces aren't ideal seems a bit of a stretch.
I used Clojure for a while and we didn't get along despite my attraction to the language, and the hype, I guess. Clojure makes a lot of trade offs to run how it does and where it does and those choices don't necessarily add up to the experience I, or others who seem to crop up here, prefer.
While I haven't tried Kotlin, I'm aware of it and know that it isn't all that different from Scala, which is a bit more my style.
It's that "on and off" that killed you. Dig in, practice, get better, and it will become much less of a problem. Set your goal "I will get better at debugging" and not "I will immediately be a pro".
Emacs cider repl added stacktrace filters that make you feel calm when something pops. I never did anything fancy but compared to vanilla stacktraces it's a great step.
I appreciate what clojure.spec is trying to do and think it's a good step! If this kind of thing interests you though I suggest looking beyond the code and learn the mathematics -- logic, predicate calculus, and the ideas behind formal methods! It's a wonderful tool for thinking and well worth encouraging.
Try looking up TLA+, Event B, Unity, etc, etc. They've been working on some neat maths and the tooling around it these days is amazing!
For more hybrid language/spec approaches Agda and Idris are super fascinating.
"In my humble opinion clojure.spec is the most VPRI-worthy feature Rich Hickey has shipped since delivering fast persistent data structures." Love this quote
It means you may soon have amazing error messages when working with Clojure. You'll also be able to write your code in such a way that it gives you amazing error messages when some part of it breaks.
The only worthwhile way is using Martin Klepsch's "Tenzing" template, but using a template is not a solution.
https://github.com/martinklepsch/tenzing
I worry about this since the power of Lisp and Clojure is how easy it is to experiment. Starting a C project is just doing a basic Makefile, a javascript project is just `npm init`.