Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Using Clojure in the command line with Babashka (karimarttila.fi)
152 points by Borkdude on Sept 2, 2020 | hide | past | favorite | 36 comments


Clojure is a recurring gravitational pull for me. I haven't had time to properly explore yet but I will before end of year.

It's not a "first" language like JS, Python, Java, C#, Ruby etc. so the community appears to benefit from a higher than average level of skill. In the same way that i imagine Erlang or Elixir might enjoy the benefit of more capable than average users.

The fact that entire paradigms can be implemented as libraries is attractive to me - it suggests to me that the Python 3000 or C++11 problems are unlikely to affect Clojure.

For example, Go is a language I find interesting. The whole CSP concept makes a ton of sense to me in writing enterprise software - mainly from a "keeping the architecture cheap to own and change over time" point of view. I appreciate this isn't where CSP was targeted but it lends itself very well IMO to solving this problem because you could decouple components and grow them independently so effectively.

Go is an entire language but core.async is just a library in Clojure, they didn't have to change the language to implement this which is mind blowing to me.

Another example is types. Only some of the code I write actually benefits from types AND most type systems in "enterprise" languages are pretty anaemic. Typing is also really hard to do well, like really really hard. You had a problem, you adopted typing and now you have two problems is generally my thinking here.

Scala was to be my saviour here but that missed the mark quite spectacularly and the compiler is still ridiculously slow today but then along came TypeScript which is pretty darned awesome IMO.

Thanks to TypeScript and modern Python i had learned that i wanted "optional typing" and i was undecided about type erasure at runtime - i don't immediately see a problem with it, i've written probably thousands of lines of Java with generics then used reflection at runtime without losing sleep, but it fills an awkward space in my head - to invest in types but only benefit at compile time seems like a miss.

In Clojure i can follow the TypeScript model but actually take it further to runtime.

This post is too long already. Long way to say Clojure appears very interesting.


The clojure community has robust tooling around types and similar mechanisms. There is core.typed, but I honestly don’t know of many who use it in production.

More recently, clojure.spec was released (https://clojure.org/about/spec) which I see the community starting to coalesce around. It’s a nicely-designed way for doing validation and property verification at runtime, and imo avoids the “awkward space” problem you mentioned. By creating specs, you get validation, nice errors, and generative testing (almost) for free. Because Clojure development is often repl-driven anyway, there’s not a huge benefit in having compile-time types, at least if you subscribe to the philosophy behind clojure.spec.


I like Clojure quite a lot---for a while, it was actually my main language.

Ultimately, however, I largely gave it up because the library ecosystem just doesn't feel quite right to me---at least at the time I was writing a lot in it (2016-7ish), most of the popular libraries seemed to have a very strong refugee-from-Java sense to them, as in, very influenced by ideas more commonly seen in oo-land. For example, people loved to use (and, more importantly, bundle in other projects) libraries like component/mount that attempt to implement ideas like dependency injection and other design pattern/architectural concepts that just... don't really feel lispy. I remember trying to build a fairly simple web application with one of the main frameworks and just having no clue which piece of the hyper-complicated and indirect code that the lein template built for me I'd need to change to debug some problems with the database. Alas.

(To be clear, I'm not saying this is wrong! It just wasn't for me.)


I’ve been playing a lot with the idea of just using ClojureScript for everything, both backend and frontend.

Shadow-cljs [0] makes interop with the NPM ecosystem a breeze, and ClojureScript is stable, mature, and feature-complete in its own right.

Although you lose out on some of the features of the JVM (threading), you get access to a lighter weight runtime, and the massive world of JavaScript libraries (which don’t bring much OOP baggage with them).

I’m not sure how viable it is long term, but so far it’s been fun, and pretty trivial to write simple wrappers over JS libraries (when repeated direct interop doesn’t make sense).

Somebody else in here mentioned that Clojure isn’t a “first” language, but I think if there was an “easier” (yes, I said easier, not simpler) path to get into it, more people might pick it up as their second language.

[0] https://github.com/thheller/shadow-cljs


I can only say glowing things about Clojure. If you have any interest at all, I strongly, strongly encourage you to try it out. braveclojure.org is a decent start, and it’s also fun to try out the problems at 4clojure.org.


I bought “Clojure Applied” and so far i’ve completed the first chapter on records and types.

The first chapter ticked a lot of boxes for me - i get a strong impression that the “Clojure way” is pragmatism over ideology. I like that.


> Clojure is a recurring gravitational pull for me.

You're in love


After working with it for a couple years now, I still really appreciate it.


I'm discovering the language these days and I really like it.


>You had a problem, you adopted typing and now you have two problems is generally my thinking here.

When you are working with types, it's like you are working out a complex puzzle. Each time you've solved a part of the system you've created more puzzle pieces in other parts. Solving this meta-puzzle feels nice and productive, especially once you get the hang of it. But it's not really solving the original problem, it's just managing the accidental complexity.


Sometimes it feels hyper productive - when you’re working out the practical details of how to model your problem, typing feels like you have a smart assistant helping you think, or at least organise your thoughts more clearly.

When you’re changing some code that’s been around for a while, or writing adapter type code, or any kind of SERDES type operations, it begins to feel less like helpful guard rails and more like unproductive yak shaving. After the initial creation, most of my code changes fall in the latter camp.

I definitely think having a comfortable way to say “here, in this code, i want types. Over here, i don’t care” is a control missing in many languages. You can certainly fake it (e.g. string typing or everything is object type) but not comfortably like for example in TypeScript.


For me the most interesting part is how it gets rid of the JVM startup time.

So there are two parts to the solution: The first part is Small Clojure Interpreter (sci) which is a "(subset of) Clojure written in Clojure", so kind of McCarthy for Clojure. A cool project in itself but it still needs a VM to run.

So here is where the second part comes in: the GraalVM produces native binaries from various things, including Clojure code and SCI is simple enough to work with GraalVM.

It's cool how these two pieces of tech combine together to create something greater than the sum of the parts.


The startup time of the jvm is negligible. This myth keeps coming up. Most of the startup time is Clojure itself.


This high startup time in Clojure is a side-effect of the JVM. If you have a hello world program that does nothing it is indeed fast.

If you are running Clojure you need to run at least clojure.core. Loading that is handled - surprise - by the JVM. As would be expected, if you run other implementations (like Dalvik), this can change dramatically (for worse, usually).

So it is correct that loading the JVM itself is not what's taking most of the time. But loading take so much time because of the JVM.

https://blog.ndk.io/clojure-bootstrapping.html


Whenever "Clojure startup time" comes up, people are quick to point to one thing: either it's the JVM "bloat," or that it's nothing to do with the JVM and is actually all Clojure.

Here's a pretty good (if a bit outdated) analysis on Clojure startup time: http://clojure-goes-fast.com/blog/clojures-slow-start/

The question is, could Clojure be rewritten in a way that loads significantly faster with the same behavior on the JVM? I'm not sure.

And especially on big projects, the lack of DCE on the JVM can lead to loading a ton of useless code that significantly impacts startup time outside of the Clojure language.


A slightly simpler explanation: GraalVM solves the startup time but clojure's built-in eval wouldn't work after compilation so you need to reimplement it in a simpler way.


Are you certain that’s the case?

See: https://github.com/BrunoBonacci/graalvm-clojure#readme


That's a table of library compatibility w/ GraalVM, one reason for incompatibility can be use of runtime evaluation.


I think it's related to dynamic loading of classes (and since Clojure functions get compiled to classes, this might be the issue), not really evaluation per se. But I haven't really looked at Graal native image for a while, so I'm not sure what's the current status.


I've been looking again at Clojure recently, and it seems to have that feature of lisps that their language feature-set seems to just settle over time; unlike Javascript and even Python, it's pretty much the same as it was a few years ago. Instead, it's implementations like Babashka that are providing most of the innovation.

I think this is a good thing! Though I do worry a bit that Clojure can look more moribund than it is. I see a lot of people exploring the language for the first time saying "this tutorial is from 2013, is it still valid?". The answer is mostly yes, but with some changes in tooling that can be dispiriting for beginners, and may make them think the whole ecosystem has gone rusty.


It’s pretty much by design, as the syntax and standard libraries with lispS are intentionally small. In other languages, you see new language syntax or semantics being added, while in a lisp it’s pretty much unheard of to change the syntax, there’s so little to change.

Macros can do a lot of stuff, and as such libraries themselves can add pretty elaborate functionality.

Having said all this, what I do notice with my experience of Clojure is that there are certain “trends” in how you write code. The language itself may not evolve that much, but the styles people write code in it, and how they solve problems, certainly does.


I've been using Clojure in isolation and I have "style anxiety". Can you elaborate (or point me somewhere) on the trends and changes in style you've observed? Thanks!


I think the closest we have to a "agreed upon style guide" that is written down, is https://github.com/bbatsov/clojure-style-guide


I think this tends to happen to all lisps, at one point or another. Many new language features in lisp languages don't have to be added to the compiler itself and can instead be distributed as libraries. Compared to JavaScript and others, where if you want something new, it has to go through the compiler/VM/interpreter.


Here’s some analysis to back up what you’re saying regarding stability: https://www.groundedsage.dev/posts/the-clojure-mindshare/#he...


This is one of those tools that proves that explicitly solving for ergonomics really makes a huge difference.


We've been writing clojure cli utilities a lot since babashka came out. It's really nice killing some of our reliance on bash.


Yeah, babashka has become our go-to form of scripting in the past 6 months or so. Super handy!


This is a nice post. I wrote my own exploration of bb (Babashka) recently here, and it includes some comparisons to Python and JVM Clojure as well:

https://amontalenti.com/2020/07/11/babashka


Babashka is only half the magic.

It is built up from SCI (small Clojure interpreter). Which is a Clojure interpreter written in Clojure. That's super cool!

Sci is awesome, and it allows you to create custom Clojure interpreters with whatever subset of Clojure + any additional functions and macros of your liking.


Seeing this makes me wonder if a concatenative/stack (factor?) variant wouldn't be even better. For small script .. when you need larger you go full lisp.

ps: just installed it on my old core i3.. and omg, this loads faster than sbcl.


BB is really cool, I just used it to run an ETL-type process I wrote, and I didn't had to change the Clojure program at all. Also, the built-in batteries were just what I was using anyway (JSON parsing most importantly).


He concludes that Python is just more practical and superior for these scripting tasks but went through all the trouble because he likes Clojure.


That is not the conclusion at all. Literally the conclusion:

> It’s nice to have another scripting tool in my toolbox: Babashka. Time will tell if I start using Clojure instead of Python as my preferred scripting language, thanks to Babashka. At least in this exercise Babashka did really well.


Actually, „because he likes the language“ is a good enough reason. At least for someone with a hacker spirit. Liking a languange will motivate me more to look into all corners of that language and put my heart in exploring it instead of knowing as much as I need to get the job done.


(this commenter shows up in most Clojure threads, to bash Clojure)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: