Live data from Hacker News

Joker: A small interpreted dialect of Clojure written in Go

joker-lang.org

41–50 of 73 posts

Re: Joker: A small interpreted dialect of Clojure written in Go

#41
Some clarification for those interested:

Joker is not a dialect of Clojure hosted on the Go runtime. Joker is a dialect of Clojure using its own interpreted runtime, that happens to be implemented in Go.

That means you cannot use Joker to build statically linked Go binaries and you cannot interop with Go from your own code.

And if you're interested in the idea of having an interpreted implementation of Clojure, for scripting use cases and fast startup, I would recommend you look into Babashka instead of Joker: https://babashka.org/

Joker was the best interpreted Clojure prior to Babashka, and nowadays, I would recommend Babashka over Joker all the time.

The reason Babashka is better than Joker for this is that it is implemented in Java, so it can directly reuse the existing Clojure implementation and libraries as-is as part of its interpreter. That allowed it to quickly surpass Joker in the amount of Clojure features it support and libraries it exposes, and continue to quickly keep up with Clojure. I'd also say the main developer behind Babashka is more active.

Even though Babashka is implemented in Java, it is still a single statically linked binary runtime that starts extremely fast and has a small memory footprint.

Kudos to Joker though for pioneering the idea and leading to Babashka.

Edit: And if you were excited because you thought this would let you write compiled statically linked binaries in Clojure, well for that you want to look at GraalVM native compilation which lets you compile real Clojure programs as low memory, fast startup, statically compiled single binaries: https://github.com/lread/clj-graal-docs

Re: Joker: A small interpreted dialect of Clojure written in Go

#42
post #34
post #20

Earlier quoted context omitted.

I think recreating Clojure point by point is not optimal. There is some middle ground between full on Clojure and the straightjacket of the STL datastructures. - Clojure is kinda difficult to reason about performance wise. The lazyness.. some weird corner cases (like how `(first ..)` is slower than `(nth .. 0)` - The STL is very strict on the "zero cost abstraction" front. Maybe something like Immer but with some syn…

> some weird corner cases (like how `(first ..)` is slower than `(nth .. 0)` I would not consider this a corner case; it is stated in (first) docstring. (first) will create seq [1] object, then it will fetch the first element. (nth), on other hand, will not create seq object. > The STL is very strict on the "zero cost abstraction" front. Many edge cases which are lost in myriad of classes. For example, vector will al…

I think zero cost in this context means compile-time polymorphism (templates) instead of runtime polymorphism (virtual functions).

Re: Joker: A small interpreted dialect of Clojure written in Go

#43
post #34
post #20

Earlier quoted context omitted.

I think recreating Clojure point by point is not optimal. There is some middle ground between full on Clojure and the straightjacket of the STL datastructures. - Clojure is kinda difficult to reason about performance wise. The lazyness.. some weird corner cases (like how `(first ..)` is slower than `(nth .. 0)` - The STL is very strict on the "zero cost abstraction" front. Maybe something like Immer but with some syn…

> some weird corner cases (like how `(first ..)` is slower than `(nth .. 0)` I would not consider this a corner case; it is stated in (first) docstring. (first) will create seq [1] object, then it will fetch the first element. (nth), on other hand, will not create seq object. > The STL is very strict on the "zero cost abstraction" front. Many edge cases which are lost in myriad of classes. For example, vector will al…

I mean, I didn't say it's undocumented behavior :) but when the default performance of a basic function like `first` is not O(1) then that strongly suggests that performance is secondary to composability/flexibility

(I'm actually not clear why it doesn't alias to `(nth .. 0)` .. I'm sure there is a good reason)

In Clojure you don't really reason about vector's run time behavior at all like in C++. You can't even ensure cache locality b/c different items can have any size. You can try to be clever and fall back to Java arrays, but the documentation says "It is recommended that you limit use of arrays to interop with Java libraries that require them as arguments or use them as return values." and the language doesn't provide the syntactic sugar for their easy use with the other data structures. So it kinda looks like the language is actively trying to discourage you from using an efficient data structure.

My previous post was trying to say that maybe you could elevate arrays to be more first class, make `first` and company O(1), maybe lose some flexibility/composability in the process but find some performant middle ground between Clojure and C++. I have no idea if this is actually achievable.

btw, STL vector resizing is a total non issue ... Most of the time you'd just allocate sufficient space beforehand (then it's truly zero cost). If you need to do something dynamic and care about when it resizes then you can just resize manually as you go (that's also zero cost - b/c this resizing is unavoidable and part of your algorithm). The default auto-resizing behavior just handles the resizing for you in a sane O(1) way

Re: Joker: A small interpreted dialect of Clojure written in Go

#44
post #23
post #20

Earlier quoted context omitted.

I think recreating Clojure point by point is not optimal. There is some middle ground between full on Clojure and the straightjacket of the STL datastructures. - Clojure is kinda difficult to reason about performance wise. The lazyness.. some weird corner cases (like how `(first ..)` is slower than `(nth .. 0)` - The STL is very strict on the "zero cost abstraction" front. Maybe something like Immer but with some syn…

You see, I'd like to write more of my software in Clojure (or any functionally inclined Lisp). If there was a really good interop story with a non-gc:d native lang I could do more of it. Clojure is a hosted language, so hosting it on C++ (or Rust etc.) would be sweet for me personally. Also, I like compiler projects.

Interop with C is pretty decent, but if you want to make a C++ hosted Clojure dialect I'm all for it! The more the merrier!

Re: Joker: A small interpreted dialect of Clojure written in Go

#45
post #30

Earlier quoted context omitted.

That's true, but in my poor man tests, Babashka was in the ballpark of Python for me.

I was surprised when I realized I had overlooked the performance non-goal considering how fast it has been in my limited experience.

Ya, it even supports multi-core programming, so I bet for some use case it can outperform Python by parallelizing.

Re: Joker: A small interpreted dialect of Clojure written in Go

#46
post #44
post #23

Earlier quoted context omitted.

You see, I'd like to write more of my software in Clojure (or any functionally inclined Lisp). If there was a really good interop story with a non-gc:d native lang I could do more of it. Clojure is a hosted language, so hosting it on C++ (or Rust etc.) would be sweet for me personally. Also, I like compiler projects.

Interop with C is pretty decent, but if you want to make a C++ hosted Clojure dialect I'm all for it! The more the merrier!

It does already sort of exist: https://ferret-lang.org/

Re: Joker: A small interpreted dialect of Clojure written in Go

#47

Earlier quoted context omitted.

What C tricks are used? I'd be fascinated to know some details.

For one thing you have C unions. You can store different types in the same place and use information stored elsewhere to know what the bits really mean. It’s horribly unsafe compared to a Go interface or the tagged unions in other languages, but uses less space. Also, there is the NaN boxing trick [1], which allows you to store a value that is essentially a tagged union that is either a double, a pointer, or an integ…

I think you could do the pointerless version (the one using an int32 index) of this in go via unsafe casting.

I’ll try it out later

Re: Joker: A small interpreted dialect of Clojure written in Go

#48
post #6

Earlier quoted context omitted.

Have you tried Babashka? https://github.com/babashka/babashka

babashka also has a performance non-goal

The performance non-goal is there to lower expectations compared to compiled Clojure. Babashka's author (me) does try to make it as fast as possible within the constraints it's implemented in.

Re: Joker: A small interpreted dialect of Clojure written in Go

#49
post #23
post #20

Earlier quoted context omitted.

I think recreating Clojure point by point is not optimal. There is some middle ground between full on Clojure and the straightjacket of the STL datastructures. - Clojure is kinda difficult to reason about performance wise. The lazyness.. some weird corner cases (like how `(first ..)` is slower than `(nth .. 0)` - The STL is very strict on the "zero cost abstraction" front. Maybe something like Immer but with some syn…

You see, I'd like to write more of my software in Clojure (or any functionally inclined Lisp). If there was a really good interop story with a non-gc:d native lang I could do more of it. Clojure is a hosted language, so hosting it on C++ (or Rust etc.) would be sweet for me personally. Also, I like compiler projects.

Consider perhaps https://github.com/clasp-developers/clasp which is a common-lisp specifically designed to interop with C++.

Re: Joker: A small interpreted dialect of Clojure written in Go

#50
For me the most interesting compilation target for Clojure is Dart. Writing Flutter apps in Clojure would be the dog's spherical objects. David Nolen thinks likewise and has commented on it in a recent defn podcast. Christophe Grand and Baptiste Dupuch are the brains behind it: https://twitter.com/cgrand/status/1350063059864346624?lang=e...
Post reply on HN