State of Clojure 2020
clojure.org
clojure.org
I've always been an emacs user, so clojure was a match. Emacs + cider for repl-based development has been an amazing productivity boost. I wanna shout from the rafters and tell everyone that this is how it should be done! I don't even want to imagine working in another environment anymore.
The Lisp enlightenment is real, in that I started to see code differently -- more in the sense of homoiconicity. I realized I began to lose the distinction between code and data. Everything just merged. The clouds didn't part nor did I ascend to a higher level of being as promised -- but it was definitely a "whoa" moment.
Enough of my blubbering, just wanna say keep up the good work! The language has brought back the fun of programming for me.
I also use parinfer (https://shaunlebron.github.io/parinfer/) rather than paredit, which makes editing Clojure a bit like editing Python. Rather than memorising commands for structural editing, you instead adjust the whitespace using tab/space/delete and the parens reorder themselves accordingly. You can still use paredit commands while having parinfer enabled, but I rarely have a need to.
This is definitely the least intimidating strategy for ecosystem newcomers.
Quickly figuring out if the model is good, I'd start with a map. If I'm feeling fancy I'll add a schema, if I need to I'll convert to a Record. Because of the way the language works you can probably make those changes without significant logic changes. Its a remarkable language.
Structure in clojure is rather implicit. The benefit is that you can nail down structure where you need it typically with spec.
A helpful hint could be that keywords can be namespaced. This can lead to better uniformity when structuring maps.
Also your dev environment and approach matters here a lot, since you typically run a REPL while you develop, you evaluate forms incrementally and figure out details about your code ad-hoc, which goes way beyond just dealing with names.
Hope that helps a bit.
Spec 2 should separate two concepts, schema: all of the possible keys, and selection: the keys used in this particular function, Spec is also recursive
Because spec is part of the language there is a growing ecosystem for defining specification once in spec then translating them to things like database schema, GraphQL schema, OpenAPI schema etc where they can be more "traditionally" understood
You also get other cool things like you can generate conformant data examples given a spec, and use those to create generative tests, as time goes on I think our static analysis tools will start working partially with specs to further their abilities
Specs also power things like this: https://borkdude.github.io/re-find.web/ and I think we're increasingly using them for validation, error detection etc in the core language too
I switched to Clojure a decade ago, around the time I started working with distributed systems. The best move I have ever made. I love working with Clojure and I find a large benefit in my understanding of the JVM.
Recently I've built a front end (Clojurescript) and some lambda services (Clojurescript on Node) to complement my startup (Clojure, https://operatr.io - its a tool for Kafka). There's an advantage to using a single language/mindset through your entire stack.
I spoke at the last Conj (https://www.youtube.com/watch?v=MnvtPzEH-d8) about my experience, broadly speaking my top reason for loving Clojure is that I am much more effective in delivery today than I was a decade ago. I simply don't believe I could have built Operatr in Java/Javascript, YMMV.
In technical terms Clojure's data-orientation coupled with host-interop is much more important to me than it being a functional language or emacs/vim etc. Turns out I still use Intellij (with the Cursive plugin) and my work setup has changed very little.
It's a great tool, if you haven't tried it you should have a play - particularly if you work with Java.
Reliance on the Java ecosystem has been the biggest issue for me in trying to use Clojure. I love many things about the language, but I have zero experience with Java or the JVM. A number of my attempts at using Clojure for a project have been hindered by my lack of understanding of the underlying runtime and the tools. In a way, it almost feels like Clojure simply isn't for me.
On the other hand, I've had much more luck with ClojureScript, which I'll happily use over JS any day, though the build system still occasionally mystifies me.
We're growing a scripting ecosystem in Clojure now thanks to this, it's still relatively new
As a newcomer, I've made several native binaries for different operation systems
End users can certainly run Clojure programs natively without having the JVM installed on their machine
It's not clojure, but it's clojure-ish enough that it can be pleasantly productive for little scripting tasks and smaller problems.
Once I was set up I quickly became effective, though I do tend to stay within the bounds of Cljs and avoid the host where I can.
Then eventually set up their REPLs, I know Cursive is working on this though and the nice thing there is you always have access to the "standard library" provided by the Google Closure compiler all of JS and then all of CLJS and all of npm and all of CLJS's libraries
Also, surely your 25 years of experience have nothing to do with you being more productive now than 25 years ago.
Same host environment, many of the same libraries, same developer. Different language, different mindset.
It's like taking an automatic 2.5L turbo Subaru w/aircon for a spin and then getting back into your 1.3L Toyota Tazz - daily driving won't be as fun again for a while.
Lisp "ruined me" for a few years because after trying it I couldn't stand using anything that dit not have macros and Paredit. It's like the saying, "Beware of tasting freedom; it can make you psychologically unemployable."
For example, dealing with the abomination that is JSX when I could just write a pure ClojureScript function that returned Hiccup data structures that gets rendered to HTML. And you get isomorphism for free (one codebase on client and server).
I initially switched to Clojure for Datomic (the killer app for Clojure, IMO), but now I mainly write ClojureScript and use DataScript (datalog on the client) in every project.
Datascript looks interesting. I guess you get disk persistence with event sourcing?
Surely there was an old lisp quote for this ? same goes with haskell / prolog I suppose
(I do not work in it.)
- its dynamic-ness makes it not suited to large projects IMO.
- The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out.
- Biggest wins I think are immutability by default, homoiconicity, and "simplicity"
- The repl is fine but can be a crutch -- see startup time
- Clojure still hasn't really found a niche, and I'm not sure I would ever reach for it first unless I was integrating with other clojure projects.
This is quite possibly true (I certainly have my complaints with it), but I'd love to hear more about what you're getting at when you say this.
I think they've taken the approach of making the system thorough and capable and this being a Lisp, we users can skin it with any level of shorthand that we please.
Datomic (the database from Cognitect) has a similar approach with its schema, which is quite verbose, but many folks that I know tend to simply write a function which spits out the schema, since it's 'just data'.
I tend to use Ghostwheel [1], but there's also spec-tools [2] and others.
Sure, you can import libraries to try to work around it but that's hardly ideal.
[1] https://github.com/clojure/spec-alpha2/wiki/Differences-from...
- verbosity (I think schema does this better https://github.com/plumatic/schema)
- because it tries to not be about validating types of inputs and outputs, and rather treats everything as predicates, it tends to live in a grey area between type validation and unit-testing, and I think hinders both
- it only helps at run-time
- over-abundance of macro-usage
Also I'm not sure what you mean about run-time, you can and should use spec as part of your development workflow.
Other two points are totally fair though, and the macros are part of the reason it never got out of alpha before a rewrite.
So as much as I like some things in Clojure I’d pick a statically typed language for most jobs now.
The reason I'm asking is that while my background the typical webdev stuff, I loved TypeScript. And now while much of my work is backend, it's similarly dynamically typed by default because I'm using Elixir. I've dabbled in Elixir's 'spec' and quite enjoyed some of the benefits Dialyzer/Dialixir offered me, but not enough to be able to tell how beneficial it is in my projects.
More than once I've run into issues that I wouldn't have had if I had used specs, but the fact that it's not a default in the community gives me pause. It makes me wonder how much advantages 'gradual typing' actually give me over just being a more thoughtful developer.
Not sure if I'm 100% won over, but it makes some good points, particularly the section about JSON. Using TypeScript at work, one of the most painful (yet quite common) things is dealing with foreign JSON from an API, which is sometimes just... wrong. And then all our carefully-typed code falls to pieces.
The argument is that in a language like Clojure you can skip pretending to make guarantees about messy external data, and trade that for simple and elegant traversal code. Probably this is why dynamic languages tend to gravitate towards "glue code" use cases while static types gravitate towards insulated systems with minimal surface area to the outside world (compilers, anyone?).
Static types are an unmitigated win, if you know how to use them.
I have a few problems with "validate then do something if its valid". On top of what that article mentions, there are concurrency issues (e.g. checking to see if a file exists, then if it does, open it... oops, race condition), and general ergonomics (if I always have to do something before I do something else, there should be a function call that effectively does both).
I don't think "but my data is dynamic" is a good argument for or against dynamic typing. It's an entirely separate issue.
In my experience, most dynamically types code makes assumptions about structure that may not hold later (better have tests!) and when it breaks some kind of validation is needed anyway to select between the correct version of the different dynamic options. In static languages this is simply typically done sooner, since the type system requires it to parse the data. (Or it’s in variants that are accessed without checking that the accesses are valid, sigh)
The type reflects invariants, constraints given for you data. The less you know, the more general type your data would have. Dynamic typing gives no advantages here safe for providing a super-general ANY type.
In case of statically typed language you would need to parse your data and construct an object representing that data just like in case of dynamically typed one. But the more you know about your data in advance, the better, especially when you are dealing with protocols and communication.
The point of a library like Pandas is that you will be cleaning and reshaping your data until it's in a format that can be properly analyzed. And that data can be very dirty and very large.
But if you prefer to clean it up in Excel or with some bash scripts, or custom written functions in PL of choice, then go for it. You still have to deal with the cleaning and wrangling. The point of mentioning Pandas is that you don't always have a guarantee on properly structured data where you can predefine the types and read it in. And you don't always have control over how that data comes to you.
But ALL data is typed, and encoding its type early in the code that processes it makes your assumptions clear and compiler-enforceable.
You're right that a typed approach to handling arbitrary input data is not right for a library like Pandas. It's a fundamentally different approach than the one Pandas takes, but it's no less valid and brings with it certain advantages.
When using the library, though, there is some level of assumed structure (otherwise your code wouldn’t work since it would be accessing fields that don’t exist. Sure data is messy in real life, so you encode that messy-ness into your structure as variants or optional that you then check, and ideally are forced to check so that you can’t silently let errors slip in).
This works in statically typed languages right now using generics, templates, types that aren’t inferred until the library is used, or whatever the specific language has to deal with this.
If your Clojure code expects an API response's json.{id,username} to exist, and to not exist is a hard violation of your assumptions and thus your code, then how is that any different from formalizing it into:
struct UserResponse {
id: Int
username: String
}
?(Damn, rest of the comment is pretty long but let me try to both sympathize and present a solution that I do like).
What I will say is that most statically-typed languages I've used do get annoying when there's no 1:1 mapping between your struct and the JSON body. I've dealt with some hairy JSON, like APIs that accidentally re-encode nested JSON strings so they become like triple escaped. Or values that can have like 5 different shapes so you really just want to try 5 different decoders until one is successful. (These cases are trivial in my upcoming code example)
For example, Rust's serde and Swift's Codeable are both pretty annoying if you want to apply arbitrary transformations between JSON response -> your canonical representation. Serde, for example, has you implementing the whole trait + visitor if you need to do anything the cute decorator DSL doesn't support. Or you implement a bunch of envelope structs even though you just want to pull out the value at "obj.a.b.c". Or, what happens when you can't simply use a codec from https://app.quicktype.io/?
JSON decoder combinators (e.g. in Elm) make JSON pleasant again while retaining static typing. https://package.elm-lang.org/packages/elm/json/latest/Json-D...
For example,
data Role = Member | Admin
data User {
id: Int
username: String
role: Role
}
-- API example body: { id: "12", role: "a", info: { uname: "foo" } }
decodeUser : D.Decoder<User>
decodeUser =
User
-- Crappy API gives us either an int or a string
(D.field "id" (D.oneOf [ D.int, D.string |> D.map parseInt ]))
-- Pull out value from nested object
(D.fieldAt [ "info", "uname" ] D.string)
-- Arbitrarily transform value and create our own error
(D.field "role" D.string
(D.andThen (\value ->
case value of
"m" -> Ok Role.Member
"a" -> Ok Role.Admin
unknown -> Err ("Unexpected role " ++ unknown))))
That almost compiles despite shooting from the hip directly into this textarea, but the point is that it's a pleasant way to decode JSON while allowing arbitrary transformation, and I think it's the ultimate middleground between static-typing and the sort of on-the-fly decision making you get with dynamic typing.Closure has definite problems that other Lisps don’t due to a combination of leaky JVM abstractions (incomprehensible stack traces, JAVA pollution), its semantics not really being Lisp semantics and its premature optimization into one paradigm that doesn’t really fit the language or most problems very well.
Whereas in a dynamic language (even an immutable one), your data could be mutated during program runtime and no longer conform to the type you expected. Yes you could re-validate it everywhere, but that requires you to exercise every code path when testing at runtime to find bugs.
I think the general argument that "You can't static type everything (like your API dependencies), so there's no point in doing it all" isn't a very solid one.
Though I will have to defend Clojure from your last paragraph. I don't think there has been a lisp that has done so much for lisps than Clojure. For example, until I used Clojure, other lisps were just like any other dynamically-typed, mutation-heavy language. The idea of just shuttling around data and transformations on that data is something that fundamentally changed my approach to writing software.
I can certainly come up with worse things to say about Clojure. I kinda grew out of dynamic-typing, for one. And this is never more obvious than if you've ever read a large or complex Clojure codebase while dying to just know what the underlying Java types are. And you're so desperate that you get excited when you see a single ^ChannelHandler type-hint.
> its dynamic-ness makes it not suited to large projects IMO
I'm really curious about this. I work on large Clojure projects, and it works quite well. We have low defects, and quick pace of change. Maybe we're just lucky, touch wood.
> The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out
If you thought of it as a type system, than it's normal you think it's not well thought out. It isn't trying to be a type system at all. It's a contract system. The goal is validation, generative testing, formal spec, parsing, etc. Not type checking. Even given a strong static type system I would still use it.
> The repl is fine but can be a crutch -- see startup time
Heard about this from some people, but dunno, it never affected me. I start my REPL at most once a day, often less then that. So waiting a few seconds doesn't bother me.
> Clojure still hasn't really found a niche
That's true. That said, it is great at everything. While it didn't find itself a popular sub-domain to dominate, I now choose it for everything. There's barely anything it can't handle well. I reach for it for everything nowadays. That's actually something I really like about it, it has huge reach.
There's various initiatives in the Elixir ecosystem, and tools such as LiveView make my life a ton easier, but man do I wish I could use Elixir as flexibly as Clojure!
Which is what? Scala? Haskell? OCaml? F#? Typed Racket? Idris?
I think Clojure really hits the sweet-spot between stupidly boring, "pragmatic" PLs and idealistic, novelty, academic ones.
Haskell is awesome, but realistically it is really difficult to quickly train someone to the point of them being able to write production-ready Haskell code.
Scala has its own warts (besides, Kotlin seems to be slowly eating its pie);
OCaml still struggling to get any recognition in the industry, despite all attempts from a few good players.
I disagree with the notion that Clojure is not suited for large projects, but I agree that Spec needs to be improved (and it is being actively worked on). And I don't think it is not well thought out. Rich Hickey is famous for slowly, carefully, patiently designing things. Stability and predictability of Clojure and its standard library is phenomenal. For every single language (even those I have never worked with) I can probably name a thing or two off my head that involved breaking changes. Not for Clojure.
These days Java works just fine. Why use anything else, that neither has dev mind share nor the library ecosystem remotely the same as Java.
Half tongue-in-cheek, half serious: not having Java's dev mindshare is actually a benefit.
For me, clojure provides a simple-to-use API, high-level functional abstractions, an instantaneous feedback loop, access to a battle-tested JVM ecosystem, and more than fast enough performance. This is its killer combination, in my opinion. The immutable-by-default data structures are also an underappreciated boon for writing multi-threaded code without breaking a sweat.
This actually one of the things I like about Kotlin - many of it features are similar in effect to pre-generated boilerplate - but it lives as part of the run-time, not in your code.
On the flip side, a new version of Kotlin could potentially introduce/reveal bugs - but at least you don't have to deal with four different generations of outdated boilerplate when diving into old code...
If I want JVM interop, I'd use Scala. Or yeah, as you mention, Kotlin isn't so bad either.
For a fun project, I'd use Idris, Haskell, or Rust (of course, many people may not consider Rust a functional language)
On the frontend, Elm or Purescript.
> I disagree with the notion that Clojure is not suited for large projects
True, large projects can be maintainable or unmaintainable in just about any language. But there's a reason dynamic languages like javascript, python, ruby, and php are seeing such heavy investment into gradual typing: it's helpful to have easy-to-digest information about the shape of data going in and out of functions. Almost nothing about spec is immediately self-evident -- it just gives you more code to read and reason about.
A former colleague once said that many companies using Haskell don't really want to spend money training existing developers to learn Haskell; instead they want to hire programmers self-motivated enough to learn Haskell on their own.
We have one 100k loc project in Clojure and one 500k in OCaml and I would choose OCaml over Clojure any time any day. I really like that things don't brake accidentally when somebody changes code, and the amount of tests you need to write are orders of magnitude smaller.
> OCaml still struggling to get any recognition in the industry
So does Clojure. Both are extremely niche below the radar languages, so it's not a dichotomy really.
There is a dichotomy of choosing OCaml or Java/C#, but Clojure would be on OCaml's side of things here.
Today, if I'd need jvm, I'll simply take modern Java.
> despite all attempts from a few good players
The few players we have (JS, Facebook, LexFi, Citrix, unis) are doing well enough producing enormous amount of high quality libraries. At least in the domains of system programming OCaml is tightly packed with all sorts of batteries.
100% Kotlin. Doesn't even have to be Clojure or ABCL.
To me Clojures sweet spot was dynamically typed, functional programming with an extremely large ecosystem of tools (due to Java interop).
I used to think only languages with good algebraic data types (mostly ML based) where worth my time... but types can really become an straight jacket in large programs and become tedious to use. Clojure code, when reasonably well structured, is not a bad solution for a lot of varied problems, and is beyond great for personal projects! Low ceremony, fast feedback loop, excellent performance, and a huge library of ready made quality libraries.
While devs in other programming language ecosystems either debate endlessly or rush to add more "features" to their language, Clojurists quietly make things that simply work.
If you follow the trends and dig a bit more, you realize that Lispers carefully filter out good ideas and put them into use, they know how to tell the difference between a brilliant idea and a fad.
And there's no fanaticism and worshiping of language creator. Only respect and gratitude.
Clojure can be typed https://github.com/clojure/core.typed/
> - The currently-accepted attempt at a non-type-system-type-system 'spec' is not well thought out.
Spec is not a substitute for a type system. It is much more loose, since you are just dealing with implicit predicates but at the same time it is more powerful than what mainstream type systems give you in terms of validity and expressiveness.
So I'm getting the most leverage out of types when they solve: - Static analysis - Specification
Clojure's static analysis tools do use type analysis since the underlying primitives are typed, but a good static analysis does type analysis and other checks so types aren't the be all end all there
Personally I love types that track to primitives, the problem with types is when you let developers use them to express their Person class or whatever, your Person class is not as fundamental as that language's int class, for example, the semantics are completely different between your primitive types but not your Person class vs your Address class
Anyway what I wanted to say is I have yet to meet a type system where I'm happy with its specification capabilities without moving into slot based programming or so much syntax for the types it becomes overhead to read them, i.e. you can write complete programs in TypeScript's Type system
Spec is the competitor for type specification here, it is much nicer you can translate it into API, GraphQL, Database schema etc but it's still getting work for Spec 2, but you can generate data examples out of it, use it for generative testing, use it for parsing, validation etc It's good if you have that specification problem
The static analysis problem is getting more and more attention through clj-kondo and inbuilt analysis via Cursive and other editors, they'll stop you from doing things like (inc "hello world") because you can't increment a string, I think this feature is actually why people love types so much the fast feedback for silly mistakes is soo good and we have that too
What we didn't do was complect those two things together, it also doesn't mar our thinking, I don't think there's an end game where a sufficiently smart type system where it eliminates all programming problems, the next breakthroughs will be in data, not types see the whole area of deep learning, but we're so in love with types we create languages where their USP in the type system
Also please don't read this as types are bad that's not what I'm saying, or tables are bad, letting them consume your thinking means you start thinking about problems in terms of typed or not typed boolean thinking instead of nuanced, since nearly all languages are "typed" at runtime and static analysis time anyway
Monolithic design is not a good way to structure projects IMO. Any large project can, and should, be broken down into small isolated components that can be reasoned about independently. Not only does this reduce overall mental overhead of understanding the code, but it also makes it much easier to reuse. Static typing appears to encourage coupling via the types due to them having to be expressed globally. So, it naturally steers the design towards building a monolith.
I also completely disagree that the REPL is some kind of a crutch. It's a strictly superior workflow because it allows you to put the application in a particular state, and then develop functionality against the state you've already built up. Having to restart the application, and rebuild that state every time you make a change is simply not a productive way to spend your time.
Finally, the claim that Clojure hasn't found a niche is pretty weird seeing how it's used all over the place by companies big and small. It's certainly found a very nice niche in space of web development and companies like Apple, Atlassian, and Walmart are getting pretty good mileage from it.
This is around 5/10 years ahead of its time in the general PL community.
It's probably somewhat decreased my earnings compared to trendier languages, and I worry that the market for Clojure developers is small and fragile which might bite me in the future... but I've never been happier doing my job than I am since I've started working with Clojure!
- The language itself is beautiful to read and write, minus a few warts that take some getting used to here and there.
- The jobs that are there tend to be smaller teams at larger companies which is my preferred type of setup.
- The community is mostly self-selected and tends to be fairly friendly.
- Working with the REPL feels like having a conversation with your computer, teaching it how to do what you want it to do vs fighting with a compiler.
- I like the "batteries not included" ethos - everything is a library and I can pick and choose what I like.
- Working on the JVM means I get access to untold thousands of Maven packages plus the JVM itself which is a very solid system.
Odd, considering that Clojure is the best paid language according to the Stack Overflow Developer Survey:
https://insights.stackoverflow.com/survey/2019#work-_-salary...
https://insights.stackoverflow.com/survey/2018#work-_-salary...
But you do have to be diligent and spend some time to search for them. That stack you mentioned is definitely extremely common here too. And in the uni department where I work now the existing research code is still all LAMP stack and Java, so I'm trying to skip a generation of tech and get straight to modernity by introducing Clojure here.
I found the Clojure community is really fun. I tried Fulcro/Pathom, it's like GraphQL but much more flexible. And also there are open-source alternatives to Datomic like DataScript in the browser, and Crux is also very promising. I always wonder what if we bring some Prolog/Datalog to day to day programming, and those are the ideas well implemented and polished.
Another interesting community I found is Elixir, Phoenix Liveview is very fast to not only prototyping but also achieve high performance.
As a comparison, most statically typed languages community spent most of their time climbing the lambda cube, or in another word, struggle with correctness and expressiveness. It's not necessarily a bad thing - on the contrary, it's applausable. But I found most of the statically typed languages are focusing on those similar things rather than exploring new directions with would be more weird and fun.
The tooling situation definitely needs some work. Compared to the batteries-included setup of Cargo or NPM, it felt pretty slapdash. The fact that Leiningen is both the go-to and unofficial was a tad bewildering.
Once I got set up, the development experience in VSCode was also quite underwhelming. I've never liked CLI editors and I didn't want to shell out for IntelliJ, so it was my best option, but it didn't offer much beyond indentation and inline eval (the latter was cool, but the lack of basics like autocomplete outweighed it).
These factors obviously aren't as relevant at a company that already has a standard way of doing things, and maybe the situation has improved since I tried, but it really put a damper on the "hit the ground running" experience which is important for gaining initial traction with developers.
All of that said, I can see the language being really pleasant to use in the right context. Most of my hobby projects involve GUIs and/or graphics, so I haven't done any more Clojure since. If that weren't the case maybe I would've put in the extra time to really figure out a good environment for it.
> The fact that Leiningen is both the go-to and unofficial was a tad bewildering.
Clojure now has an official, built-in CLI that mostly supersedes Leiningen.
You can just install Clojure itself, create a 'deps.edn' (which can be as simple as '{}') and get going at a REPL by running 'clojure' or 'clj'.
Packages are auto-installed - in some ways I find it even easier than node, where npm is a separate tool/install sometimes. :)
> Once I got set up, the development experience in VSCode was also quite underwhelming.
There's a VSCode editor plugin called Calva[2] which has developed pretty thoroughly over the last year or so. Outside of IntelliJ/Cursive, it's starting to become one of the more feature-complete systems and it has plenty of features that you'd expect.
[1]: https://clojure.org/guides/deps_and_cli
[2]: https://marketplace.visualstudio.com/items?itemName=betterth...
It definitely has intellisense/autocomplete now, so that at least is one point of difference from when you last saw it. :)
This, coupled with a REPL driven development gives you a very interactive and agile dev experience.
Just in case others were looking. It probably helps to start with IntelliJ + Cursive, instead of trying the lesser featured VSCode and Atom support, or the more difficult to master Emacs + Cider combo or the many VIM alternatives.
On a different topic, I’ve read that almost 50% of computer programming work is just building database views and providing GUIs so non-technical people can interact with databases. Making this work easy used to be a major goal of multiple corporate products. One reason VisualBasic had such a devoted following is that it made it so damn easy for novices to build a GUI for a database.
In recent years the industry has moved towards HTML as the default way to build an interface for anything on a network, but HTML continues to lag when it comes to forms. You have to use a mountain of JavaScript to get the form functionality of VisualBasic. We’ve actually gone backwards on ease of creating database GUIs. Yes, in theory you can use something like React to build forms, but the dev using VisualBasic would be done with the project while you’re still waiting for npm to finish downloading your dependencies. And React requires more skill. VisualBasic had a wonderfully gentle learning curve. Likewise, Ruby On Rails (and other frameworks including Symfony and Django) have scaffolding that allows the fast construction of HTML forms, but, again, HTML is a limited method of building forms.
I’d love to see Clojure succeed in this space, building corporate GUIs for databases. How to do that? Is a new GUI technology needed? Towards that end I’d love to see EDN used as a config language to define a scaffold for forms. There are elements of Swing that worked well and could be rescued from the larger wreckage of Java GUIs. And JavaFX represents a huge “road not taken” for the whole industry.
The tech industry has had eras when it focused on opening the door to beginners. Those eras gave us HyperCard and Flash and VisualBasic. Right now the industry seems to be undergoing an episode of gatekeeping. We are requiring beginners to learn React, which is a big eco-system, and also stuff like Docker, which is tough for beginners. The point of making so much tech mandatory for even simple projects inevitably keeps some people from discovering the joy of programming.
Clojure is the most fun language that I ever worked with, and it’s conceptually simple and it has EDN as a data literal language, all of which is a kindness for beginners. If it also had a simple method for building GUIs I believe it could help lead the industry into the next era when we again focus on making programming accessible for beginners.
https://github.com/cljfx/cljfx
It's built around JFX, but it's very Clojure-y (much more than the alternate Swing library). I've been making small GUIs with it and it's convenient and "just works". The only issue I've had is when some callback blows up somewhere in the guts you don't get clean call stacks so it's very hard to debug, but then.. isn't most GUI code? You just need to work incrementally which is easy to do with a REPL
I also wanna say that the CLJFX maintainer is very friendly and responsive and he's been very helpful with all my dumb questions. I thought it's be one of those libraries that gets "finished" and then archived, but he's been keeping at it and releasing new versions for a long while now
The design-paradigm is essentially the same (React-style). You still have a state atom and you're still making a map structure of your GUI elements and then hooking up callbacks to events. I find it's really easy to use, but at the same time I have to admit I don't really get how it works or how to debug the system. It's a bit of a black box for me. You feed in a data structure and it make a GUI for you and functions get called when stuff happen/changes.
Clojure has great potential for what you're describing.
Forms put data into databases, analytics take it out and do stuff with it. That's kinda like a dual, if you squint and wave your hands enough.
Where VBA is concerned, forms to put data into the database, and analysis/report programs that take it out again, have always been the main use cases.
1) You like EDN and would like it to become popular.
2) You would like Clojure to occupy a similar place RAD and 4th gen languages tried to occupy decades back. Not saying this in pejorative way, since I'm sure a big chunk of HN has come to appreciate VB a lot more now that is not such a big thing as it was in the past.
--
Regarding 1), I feel like it is a little bit hard that EDN will become popular, mostly since it goes so much hand in hand with Clojure and its APIs. Plus, in order to talk EDN to browsers, it makes sense to pack it in JSON and then use some clever encoding (like [transit] does). One of the tricky parts of using ClojureScript is understanding the handling of data coming from APIs.. I'm only now starting to get into this but I think is not trivial, and this is why projects like [cljs-bean] exist.
--
Regarding 2), I'm also not sure that the same target market that embraced visual basic and continue to use it will really ever be very happy with Clojure and its ecosystem, since there's not too much emphasis in making things plug and play, is more like a choose-your-own-adventure community :-). I guess it would require a very nice package to market it, the way rails did with ruby.
--
transit: https://github.com/cognitect/transit-format
cljs-bean: https://github.com/mfikes/cljs-bean
Looks like you’re finding HyperFiddle[0].
Quoting from the website:
> Low-code databases, developer-grade
> Hyperfiddle lets you make lightweight database applications out of just database queries.
It’s Clojure based, uses the EDN notation, and doesn’t require writing a lot of code. It basically translates the Datomic database[1] queries into tables (which can be exported to react components and styled appropriately).
"For a high-end UI, use React.js. The above cryptocurrency invoicing MVP generates QR codes using a javascript module from github. This blog post is a hyperfiddle and includes React.js components, CSS, HTML5 video components, mailchimp & twitter integrations"
Hyperfiddle is basic scaffolding of the type we've had for 15 years thanks to Ruby On Rails. I'm suggesting it would be good if we could get beyond this level, since we have been stuck at this level for a long time.
Hyperfiddle does use EDN for configuration, and I really like that part of it, but it's still using HTML, which, as I said above, doesn't give us the form functionality that VisualBasic did.
* 2020: 2519 responses
* 2019: 2461 responses
* 2018: 2325 responses
* 2016: 2420 responses
OTOH, Rich Hickey has influenced(in good way) a generation of programmers and doesn't get credited enough.
Aside from the entire Java ecosystem? I know using a Java library from Clojure isn't exactly plug-and-play, but that's a bit dismissive. Java has way more code written in it than JavaScript for most things.
For me the reasons for not using it as a primary replacement are: I get dizzy from interop; it is much more convenient to embed Clojurescript into JS than the other way around. And secondly and more importantly I cannot use it in collaboration.
Clojure also has debuggers: you can debug from either Cider or Cursive, for instance. I'm not that familiar with debugging outside of those two though.
With most setups nowadays (create-react-app/Webpack, Parcel, etc.) and "hot-module reloading" that's no longer really the case...
...but that doesn't take away from your point, because it can have some truly odd effects on the debugger haha
Nothing is a replacement for a good, "true" REPL. Being able to evaluate any [sub]expression, without any kind of ceremony, anytime, from anywhere in your code is incredibly liberating. Who never used Lisp seriously, would never understand how amazing that workflow is.
I think REPL is the main reason why we still have Lisp dialects. After over six decades Lisp is still alive and thriving.
In that way I feel Clojure's defaults to immutability go a step beyond what's offered in most Lisp's setups. Not everything "just works" though, thought is still needed in order to have certain things be easily reloadable. I'm talking things like using "defonce" forms and having a setup like described here [1].
I'm still exploring this space but I think the gist of it is to create a direct acyclic graph of dependencies, so you can start and stop exactly what you need, not unlike what build systems do.
Javascript is still a very difficult language to work with. Arguably, it is even hard to rate the language as a production-ready PL. The hidden cost associated with building, maintaining, and scaling JS applications is immense. There are so many inconsistencies and oddities in the language - it is not even cute or funny anymore to talk about WATs or "bad parts" of it. It gives you a false sense of simplicity, it feels like an easy language to start writing apps with, but God, very soon, you realize how awful things are (in comparison).
Clojure(script), on the other hand, is so much more practical. It only looks more difficult, but given enough patience, it brings you stability, consistency, predictability, and, most important - the incredible joy of building working software.
Notably, the majority of Clojurists are seasoned, experienced, tired, grumpy software developers. After trying different options they've finally found something that more or less makes sense. People mistakenly characterize that as a "Stockholm syndrome." Which I assure you is not. I personally use Clojure because it simply makes sense. Once it stops making sense, I will move onto something else. Who knows, maybe Javascript will evolve into something better then. But I can honestly say: "Javascript today does not make more sense." It is not a mere opinion of someone with a cursory knowledge of JS, I've used it (and many other languages that transpile/compile into it) for many, many years.
I have a kind nostalgia for JS because it was my first foray into 'proper' programming (alongside PHP), and I quite like TypeScript. I'm comfortable enough with JS that I can open the dev tools, write a moderately complex chunk of code, and save it as a bookmarklet or private chrome plugin. I'm a JS native.
But good lord do I feel so much better working in Clojure or, day to day, Elixir. There's no way I can comare them to JS and argue for the latter, even with the many improvements since the document.write() days.
Today it is possible to run Clojure on:
JVM, JS, BEAM, .NET CLR, Common Lisp runtimes;
Have interop with Python and R. There are Clojure-like Lisp dialects that can compile to Lua and C.
> you don't need the Clojure repl
If you never have experienced the joy of using "true" REPL, you will never understand the immense value of it. Non-lispy languages, sadly have no "true" REPLs.
No, it is still would be very crippled imitation of REPL, merely interactive environment. For a language to have a fully-functional REPL, it is required to have at least some homoiconicity, which JS is not.
Let me demonstrate:
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);
Can you evaluate that in JS REPL? Can you evaluate any part of it?Now, let's naively "convert" that into Clojurescript:
(ReactDOM.render
[:h1 "Hello, world!"]
(document.getElementById "root"))
You can evaluate every single part of it. You can evaluate "Hello, world!" string; [:h1 ...] vector; document.getElementById function, all these are valid Clojure expressions.
In addition to that - Clojure example is so much easier to modify and change. With just a few keystrokes, you can do a lot.That's basically the workflow - in Clojure, you bootstrap an app with some small, core dependencies, then start a REPL, connect to it and keep modifying your app while running it. That is what gives so much praised productivity boost. It doesn't matter how fast your compiler is (in other languages) - "save & recompile" workflow never going to be as good as working with a "true" REPL.
In another comment you gave an example like: (foo [a b] ((c a) b)) stating that you can eval any part of it, but if you try to eval ((c a) b), won't the repl raise an error stating a and b are not defined?
You see, in Lisp you think and operate in units of s-expressions, any s-expression can be send to the REPL as-is, "code is data" <=> "data is code". In Javascript it also adds some hurdles of things being out of scope. In Clojure, due to default immutability - the problem space is usually small, it encourages you to write smaller functions and it is much simpler model - most of the time you can evaluate anything without any preceding ceremony. In some cases you do need to set the context as you pointed with `((ca) b)` example, but it's so trivial to do so with s-expressions and it's so tedious when working with Javascript (trust me, the comparison not in favor of the latter, I've built JS apps for over a decade).
Even if you were, who am I to judge you? I myself had a chance to learn Clojure back in 2013, and then a few months later, and then once again next year, but I kept saying: "no, thanks". My thoughts were:
- JVM, yikes. JVM means Java, everyone hates Java, am I smarter than everyone else? I hate it too;
- Dynamic typing is so stupid. I've had enough with Javascript, Python and Ruby, I'm not doing that shit again;
- Parentheses. Lots of them. Too many of them. I can't see anything else. I'm drowning in the sea of parentheses.
If only I knew that:
- JVM is actually really robust and extremely good piece of tech; Try managing Nodejs clusters or deal with dependency conflicts in Python's pip. Honestly, JVM is so much nicer.
- That dynamic typing in certain context can be your friend and not the enemy. And REPL driven is much better than "save&recompile" workflow. Besides, there's Spec and it's absolutely dope;
- Parentheses bring structure, simplicity and consistency, and there are not too many of them. Javascript for example has more, and also semicolons and other shit.
Clojure like any Lisp, sadly doesn't do a well job of "introducing itself" to complete newbies. It doesn't look sexy when you're trying it out for the first time. It requires a bit of patience and the leap of faith. And those who are ready to pay that small price, soon may discover an amazing world of possibilities.
I don't quite understand this, could you explain it a little more in depth?
But I think one has to experience it, to understand what makes languages like Lisp and Smalltalk so special, what is so unique about REPL driven workflow that even Python's so much praised Jupyter can't give you that satisfactory feeling.
It's really nice to be able to run anything - from the smallest unit in your program, as well as the whole program and see the immediate feedback. For example: You can evaluate SQL DSL constructs and see what SQL query they compose into, and you can run that query and see the results (all without ever having to write a single line of actual SQL); or you can connect to the browser and change elements of the DOM (without writing a single line of html, css or js).
You can take a complex piece of code that generates a .csv, and then analyze that csv on the fly, changing columns and rows on the go, without even saving and having to wait for the compiler to pass the checks.
You can compose music and data visualizations, and etc.
The secret to this comes down to the Clojure mindset. And where I agree with you is your last statement, Rich Hickey helped push this mindset to other languages, and I'm seeing some of it slowly ramp up in other languages as well. Which is great, but it never comes together as nicely, and who knows where it's going as well.
So one reason why the respondents haven't increased much is because its not attracting enough new developers and seasoned developers are giving up too early. Which is too bad because, sure, getting over that initial hump can be frustrating at times but, boy is it worth it! Spending only a few months with Clojure has already made me a much better programmer in the other languages I use.
A few of them went off and were so inspired by REBOL that they created Red[1].
In it, the creator of Rails says:
> it doesn’t have to be a million person movement for it to be a place where you can live and breathe and work. Um, I mean, I created. Base camp when Ruby had nothing, right? In terms of the tooling that I wanted and I needed to create web applications. I just created all that stuff for myself. By hand. I mean, it’s totally doable
And that's how I feel about my choice of using Clojure.
It's also worth noting that responses are indicating that companies using Clojure are getting larger, and more people are using it professionally. This likely indicates that many Clojure startups have successfully grown into larger companies at this point. This is also reflected by efforts like Clojurists Together where companies are funding open source projects that are seen to be strategic for the community.
So, overall I don't really see anything to be concerned about here. Clojure is a niche technology, but the community around it is very vibrant even if it's not growing especially fast.
Hard to move a community though.
Do you have any idea what it takes for Slack to record the history and make it available? Is is very expensive? I know they charge individual users for the service of seeing far back into the past, not sure if they make that free for any particular communities.
Yep Arne Brasseur runs https://clojurians-log.clojureverse.org/, though integrated logs would be better of course.
Also, IMHO it's much better than slack for chatting. If only everyone switched!
Or you can search (/) and type stream:slack-archive topic:jobs
My largest issue is spec.alpha, which is far from perfect. spec-alpha2 should fix many of my complaints, so I'm looking forward to its first release.
Can someone please sell me, or assure me, otherwise?
[1] Which is also high... I can barely get a JVM project running these days with the plethora highly foreign of build tools, IDE dependencies/configurations, and language settings. Maybe that's just par for the course in 2020 on the JVM, with great power comes great complexit?
Download a Clojure jar [0], the following command opens the REPL:
$ java -jar clojure-1.10.1.jar
... you can also run the file you are editing: $ java -jar clojure-1.10.1.jar file.clj
... that's it! You can go a long way just like that, the start up time of running scripts is not that bad.Gradual improvements:
* Perhaps install a clojure package [1] instead of using the jar. The jar is nice to understand the main Clojure compiler is at its core just that, a Java program... but the package installed with brew or apt will probably (depending on your OS and how up to date the packages are) also give you a global `clojure` or `clj` command that you will see around in tutorials. Just makes it easier to follow the tutorials!
* Use rebel-readline [2]. Just follow the readme!. It gives you a nice coloured command line REPL with all sorts of cool features, but you don't need to make use of all of them at once. Take your time!
* Choose an editor with good integrations. Obvious choices: VSCode+Calva extension, IntelliJ+Cursive, or Emacs+Cider. The main thing about having a good integration is the instantaneous feedback of sending chunks of code to the REPL, although for a slow start you can do the same just copying and pasting code from the editor to the REPL!
About books: I read 3 or 4 different ones... most books I read were pretty good, some of them are available online for free! Confidence comes from writing lots of code mostly, though, not just reading. I'm not proficient enough yet that I remember the APIs yet so books help to gimme a cohesive roadmap of things to learn.
Suggested little "project": I also found this library [3] that is cool to play a bit with random things to produce some graphical output. It is a wrapper around Processing.
Good luck!
Caveat: there are many ways to do teh same thing. This comes from a focus on libraries over frameworks. Also, the whole JVM API is available to you so for a lot of things you are supposed to just use the Java APIs. Don't bother about wrappers or clojars for a start, just call Java from your clojure. Is fast, fun and easy! :-) Pick one way of doing things and do it for a while... even if it doesn't give you the "fullest of the performance" for any given task, it will be an OK vehicle for you to learn and get familiar with the ecosystem. Classical example: http servers.
0: https://repo1.maven.org/maven2/org/clojure/clojure/1.10.1/
1: https://clojure.org/guides/getting_started
So! Clojure 1.8 still works without dependencies for trying the jar [1]. Otherwise, I'd skip to installing 1.10 through the system installer.
1: https://repo1.maven.org/maven2/org/clojure/clojure/1.8.0/
Linux can use brew as well, or just download an installer shell script and run it.
Windows can use an (alpha) installer for Powershell or use Scoop via this bucket https://github.com/littleli/scoop-clojure
That's for the official CLI that works with deps.edn files for dependencies.
You can also download Leiningen -- a single shell script (macOS/Linux) or batch script (Windows).
If you use brew/scoop, I don't think you even need a JVM installed as they will do that automatically.
Wish the core team many more years of work, and I hope the ecosystem stays afloat, and prospers.
Also, kudos to never breaking my code! That quality is so underappreciated.
For a "state of X" survey it would be nice to see questions like "what are the biggest hurdles, problems, issues". For example, State of JS drills down to language features, syntax, overall happiness, frameworks etc.
E.g. the author of Rum and Datascript re-wrote his ICFPC submission from Clojure to Rust and gained 17x speed increase [1]. This could be something to poll.
[1] https://mobile.twitter.com/nikitonsky/status/114850460921383...
Most job boards have next to no entries and clojure boards are mostly US only.
https://github.com/clojure/tools.deps.alpha/wiki/clj-on-Wind...
"Currently, clj on Windows is in an alpha state."
I’m not a huge fan of the language myself, but it does have it’s strengths.
Its method for ranking seems problematic.
Are Haskell, Rust, F#, Elixir, OCaml, ReasonML, Elm, Purescript, Racket extremely unpopular?
Clojure today has more podcasts, books, conferences, jobs than any of these languages. But sure, it is a dying programming language. Just like Haskell has been "dying" for 30 years now and OCaml "died" multiple times in the past 24.
There are good reasons why they are still being used and why people get excited about them.