Clojure hosted on Go is something that I really hope gets more attention. Clojure being built on top of Java is a fine decision, but I would love to use clojure to compose the ever growing library of stable packages that exist in the Go ecosystem.
I think most Clojure implementations take the lesson from the original that to be embedded in a platform rich with mature libraries is a great strength, but aren't dogmatic about some idea that only a few runtimes are worthy of such embedding. The idea is more 'make it practical to bring a Lisp to work and integrate it with your existing projects', rather than 'create one implementation to rule them all by choosing the biggest ecosystem to integrate with' (though a consideration aligned with the letter was doubtless a factor in the choice of platform for the first implementation).
So the thought is more like: because Go is popular, a good Clojure implemented in Go will let me bring all the Clojure goodies to many additional projects with tight integration for little fuss— not 'finally a big ecosystem, the JVM has no software'.
Another way to think about it is that different platforms often have their own 'killer libraries'. Maybe for working with some Docker or some IaC tools, a Go-hosted Clojure would be especially convenient, for example.
Java is definitely not even close to being "highly expressive". It has been a very simple language, and was always very conscious of remaining that with a slow, but well-thought-out evolution, learning from others' mistakes.
Hmm maybe I'm too out of touch. You're talking about recent java (12+) vs go ?
I stopped working in java so maybe I'm too bruised by the J2EE 5 era, but what I saw from Go was an order of magnitude less verbose than my memories of java.
Java EE was/is a whole platform, framework, deployment model all in one.
Also, most of the verbosity of that early version was very high flexibility (everything could be replaced) plus an XML-based configuration. I wouldn't really count XML in Java's verbosity, nor do I think that comparing it to vanilla Go is meaningful.
For similarly scoped libraries Java is less verbose due to go's error handling being all over the place.
Maybe, but IME they tend to be lower quality than the Go ones. Java has some truly great libraries, but culturally has an entirely different approach to almost everything.
I really like Go as well, but the JVM is a lot better at hosting languages than any interpreter implemented in Go. You will likely pay a significant performance cost even for a very optimized Go interpreter. I wish this were not the case.
Thanks for the shout out. :) It's always great to see more Clojure dialects.
For those jankers wondering what Glojure means for Clojure on native, the main thing to note is that Glojure is an interpreter. No JIT or AOT compilation (right now). Looks like it's a great start for an interpreter, though. Not quite ready for prime time, given some of the todos in the code [0], but the structure of it looks quite intentional. Based on some of the code [1], it looks like the analysis could be largely a port of tools.analyzer, which is honestly a smart way to do it.
To the author, you may be interested in the clojure.core-test intitiative [2]. I'm aiming to get a good test suite for all Clojure dialects.
Glojure author here! Your analysis is spot-on :). I'll definitely take a look at clojure.core-test. As components mature, I focus on improving compatibility by porting tests from Clojure [0], but they often require modifications to accommodate differences in the host language. As you noted, there are still several fundamental features missing — most notably some core data structures. That said, the implementation is robust enough to support another non-trivial hobby project [1].
A bit more detail on some of your observations:
> No JIT or AOT compilation (right now).
I do plan to implement AOT compilation eventually. JIT, however, is more complex. Go's "plugin" standard library [2] could serve as a mechanism, but its support is limited and not without issues [3].
> it looks like the analysis could be largely a port of tools.analyzer
Exactly! Another key implementation strategy has been the handling of clojure.core. Instead of reimplementing everything from scratch, the Clojure 1.11 core libraries are programmatically transformed to work with Go [4]. However, this approach has its downsides — many functions appear to be available but are non-functional because parts of their implementation haven't yet been adapted.
And by the way, impressive progress on Jank! I've been following it closely and really admire the work you're doing.
Glojure author here. Transpiling to Go is definitely something I've considered — and am still considering! My current focus is on use cases that prioritize interactivity, which makes the existing interpreter-based approach a better fit for now. That said, transpiling parts of the system (especially the core libraries) to Go is a strong possibility down the road, particularly as a way to improve startup performance. After that, it could be made available as a public feature.
Interesting , I was rather interested in seeing something like golang which can compile to bytecode in something like java or the compiler because so many times I have heard that golang is slower because of AOT and what not when I see some comparisons b/w golang and java , and all I thought was well , why not add this feature.
I am not sure but this seems rather interesting. But doesn't this technically just implement a flavour of lisp.
I am not sure but a technical way to do this as well could be to use something like fennel which can convert to lua and a lua vm written in golang as a poor man's lisp in go
Sorry to hear it's not working for you . Please feel free to file an issue on the repo with any relevant details! While I only work on Glojure sporadically at the moment, I really value feedback like this, and I'll prioritize investigating critical issues like it.
The Go ecosystem does web and http services extremely well. It really shines when used in this manner.
A lot of clojure developers could benefit from this immensely.
On a different note there was some movement to port clojure to the CLR
And that would open the entire .NET ecosystem up, which I would love to see as an alternative to the JVM
> lot of clojure developers could benefit from this immensely.
Curious what you think Clojure developers could benefit from specifically.
Having done web services in both languages I much prefer the experience in Clojure. E.g. found error handling in Gin to be very cumbersome (AbortWithStatusJSON and such). The deployment story is nicer in Go, tho.
Clojue CLR is behind JVM support (and performance), but it has been a thing from the start, not just a "port".
Something I find problematic about the various implementations of Clojure is the lack of specs to determine a common ground.
Clojurescript doesn't use the same conventions in import/require statements: you're supposed to import macros using :require-macros or :refer-macros (I'm not even sure anymore). Conversely, `:refer :all` was banned in a prescriptivist attempt at fixing Clojure "mistakes", the rationale behind this decision being that with `:refer :all` it's not always obvious what namespace required symbols come from. Yet, with a REPL or a language server, it's very easy to get that info.
I agree with your criticism but those "mistakes" sadly come with inheriting implementation details of the hosted environment. As far as I understand, ClojureScript couldn't workaround some JavaScript limitations regarding macros and thus had to go with `:require-macros`.
A workaround is using Reader Conditionals (https://clojure.org/reference/reader#_reader_conditionals) and specifying platform differences where they matter, but it's awkward to say the least. What most projects do is to separate "common" namespaces and use the `.cljc` extension to indicate they're multi platform, and keep platform specific things in namespaces with `.clj`, `.cljs`, etc.
>What most projects do is to separate "common" namespaces and use the `.cljc` extension to indicate they're multi platform, and keep platform specific things in namespaces with `.clj`, `.cljs`, etc.
This is exactly what I witnessed when finding the example above.
Out of frustration, I tried patching shadow-cljs one afternoon and was able to implement :refer :all as well as automatically generating :require-macros when needed to some extent, but I haven't put the time to make it work fully. I don't think this is a limitation caused by the lack of a Clojurescript compiler that can run in a Javascript runtime. In short, I don't think this is an essential limitation of the way the language is hosted within its target language, unlike things like Vars, which are not introspectable at runtime in js.
Oh yeah I definitely lost an hour debugging why something doesn't work in ClojureScript: it turns out I used :refer rather than :refer-macro. It's still possible to use :refer, but it requires some changes to the library such as https://github.com/Engelberg/instaparse/commit/0cd039659dc76...
Clojure hosted on Go is something that I really hope gets more attention. Clojure being built on top of Java is a fine decision, but I would love to use clojure to compose the ever growing library of stable packages that exist in the Go ecosystem.
other related packages: https://joker-lang.org/ https://github.com/nooga/let-go
My latest favorite kid on the block is nbb. The REPL spins up instantaneously, and you can use all Node packages, it requires virtually zero setup.
Doesn’t the Java ecosystem have even more stable packages?
I think most Clojure implementations take the lesson from the original that to be embedded in a platform rich with mature libraries is a great strength, but aren't dogmatic about some idea that only a few runtimes are worthy of such embedding. The idea is more 'make it practical to bring a Lisp to work and integrate it with your existing projects', rather than 'create one implementation to rule them all by choosing the biggest ecosystem to integrate with' (though a consideration aligned with the letter was doubtless a factor in the choice of platform for the first implementation).
So the thought is more like: because Go is popular, a good Clojure implemented in Go will let me bring all the Clojure goodies to many additional projects with tight integration for little fuss— not 'finally a big ecosystem, the JVM has no software'.
Another way to think about it is that different platforms often have their own 'killer libraries'. Maybe for working with some Docker or some IaC tools, a Go-hosted Clojure would be especially convenient, for example.
Stable, often overenigneered and often hard to upgrade when used as several dependencies together.
How does a less expressive language help with any of these?
Also, I very much don't share your opinion based on experience, unless we are talking about straight up frameworks and not libraries.
A highly expressive language makes overengineering easier.
Java is definitely not even close to being "highly expressive". It has been a very simple language, and was always very conscious of remaining that with a slow, but well-thought-out evolution, learning from others' mistakes.
i guess java confused verbosity with expressiveness
Without entering the territory of flamewars, Go is objectively more verbose than Java, so I don't really see your point.
Hmm maybe I'm too out of touch. You're talking about recent java (12+) vs go ?
I stopped working in java so maybe I'm too bruised by the J2EE 5 era, but what I saw from Go was an order of magnitude less verbose than my memories of java.
Java EE was/is a whole platform, framework, deployment model all in one.
Also, most of the verbosity of that early version was very high flexibility (everything could be replaced) plus an XML-based configuration. I wouldn't really count XML in Java's verbosity, nor do I think that comparing it to vanilla Go is meaningful.
For similarly scoped libraries Java is less verbose due to go's error handling being all over the place.
Does someone disagreeing with your statement make it not objective anymore? :o)
Maybe, but IME they tend to be lower quality than the Go ones. Java has some truly great libraries, but culturally has an entirely different approach to almost everything.
Any specific examples? The stuff I've used has been broadly better in JVM land than Go land, but I mostly work with Kafka stuff.
I really like Go as well, but the JVM is a lot better at hosting languages than any interpreter implemented in Go. You will likely pay a significant performance cost even for a very optimized Go interpreter. I wish this were not the case.
Tangentially related clojure dialect compiling via C++ & LLVM: https://jank-lang.org/
jank has a progress page to give you an idea how much is implemented so far: https://jank-lang.org/progress/
Thanks for the shout out. :) It's always great to see more Clojure dialects.
For those jankers wondering what Glojure means for Clojure on native, the main thing to note is that Glojure is an interpreter. No JIT or AOT compilation (right now). Looks like it's a great start for an interpreter, though. Not quite ready for prime time, given some of the todos in the code [0], but the structure of it looks quite intentional. Based on some of the code [1], it looks like the analysis could be largely a port of tools.analyzer, which is honestly a smart way to do it.
To the author, you may be interested in the clojure.core-test intitiative [2]. I'm aiming to get a good test suite for all Clojure dialects.
0: https://github.com/glojurelang/glojure/blob/e54deb6597ceafd3...
1: https://github.com/glojurelang/glojure/blob/e54deb6597ceafd3...
2: https://github.com/jank-lang/clojure.core-test
Glojure author here! Your analysis is spot-on :). I'll definitely take a look at clojure.core-test. As components mature, I focus on improving compatibility by porting tests from Clojure [0], but they often require modifications to accommodate differences in the host language. As you noted, there are still several fundamental features missing — most notably some core data structures. That said, the implementation is robust enough to support another non-trivial hobby project [1].
A bit more detail on some of your observations:
> No JIT or AOT compilation (right now).
I do plan to implement AOT compilation eventually. JIT, however, is more complex. Go's "plugin" standard library [2] could serve as a mechanism, but its support is limited and not without issues [3].
> it looks like the analysis could be largely a port of tools.analyzer
Exactly! Another key implementation strategy has been the handling of clojure.core. Instead of reimplementing everything from scratch, the Clojure 1.11 core libraries are programmatically transformed to work with Go [4]. However, this approach has its downsides — many functions appear to be available but are non-functional because parts of their implementation haven't yet been adapted.
And by the way, impressive progress on Jank! I've been following it closely and really admire the work you're doing.
[0] https://github.com/clojure/clojure/tree/master/test/clojure/... [1] https://github.com/jfhamlin/muscrat [2] https://pkg.go.dev/plugin [3] https://github.com/golang/go/issues/19282 [4] https://github.com/glojurelang/glojure/tree/808165fdc7e65672...
Why not transpile to Go and then build a native executable?
Glojure author here. Transpiling to Go is definitely something I've considered — and am still considering! My current focus is on use cases that prioritize interactivity, which makes the existing interpreter-based approach a better fit for now. That said, transpiling parts of the system (especially the core libraries) to Go is a strong possibility down the road, particularly as a way to improve startup performance. After that, it could be made available as a public feature.
Interesting , I was rather interested in seeing something like golang which can compile to bytecode in something like java or the compiler because so many times I have heard that golang is slower because of AOT and what not when I see some comparisons b/w golang and java , and all I thought was well , why not add this feature.
I am not sure but this seems rather interesting. But doesn't this technically just implement a flavour of lisp.
I am not sure but a technical way to do this as well could be to use something like fennel which can convert to lua and a lua vm written in golang as a poor man's lisp in go
Regular Clojure relies on JVMs ability to load classes at runtime. Can Go VM do that?
I get "panic:" at the REPL once it loads. :(
The implementation of the [HAMT][1]-based vector in Go is [interesting][2].
[1]: https://infoscience.epfl.ch/server/api/core/bitstreams/f66a3...
[2]: https://github.com/glojurelang/glojure/blob/main/internal/pe...
Sorry to hear it's not working for you . Please feel free to file an issue on the repo with any relevant details! While I only work on Glojure sporadically at the moment, I really value feedback like this, and I'll prioritize investigating critical issues like it.
well, it should be lisp all the way down
https://www.loper-os.org/?p=42
Can Go easily call Glojure? Not clear from the README.
Perhaps I can finally get into go!
Clojure hosted on Go, fascinating! What must-have Go packages are you most excited for?
The Go ecosystem does web and http services extremely well. It really shines when used in this manner.
A lot of clojure developers could benefit from this immensely.
On a different note there was some movement to port clojure to the CLR And that would open the entire .NET ecosystem up, which I would love to see as an alternative to the JVM
> lot of clojure developers could benefit from this immensely.
Curious what you think Clojure developers could benefit from specifically.
Having done web services in both languages I much prefer the experience in Clojure. E.g. found error handling in Gin to be very cumbersome (AbortWithStatusJSON and such). The deployment story is nicer in Go, tho.
Clojue CLR is behind JVM support (and performance), but it has been a thing from the start, not just a "port".
Clojure was meant to be a guest language, so the more platforms it supports, the merrier.
But web and http is definitely not lacking in anything in the JVM world, given that half the business-critical backends of the internet runs on it.
In grad school I did a Haskell interpreter hosted in clojoure.
Does STM work
Good question. This is probably hard to implement, but I can't recall any real-world Clojure project that uses software transactional memory (STM).
Neat, the macros even work:
Something I find problematic about the various implementations of Clojure is the lack of specs to determine a common ground.
Clojurescript doesn't use the same conventions in import/require statements: you're supposed to import macros using :require-macros or :refer-macros (I'm not even sure anymore). Conversely, `:refer :all` was banned in a prescriptivist attempt at fixing Clojure "mistakes", the rationale behind this decision being that with `:refer :all` it's not always obvious what namespace required symbols come from. Yet, with a REPL or a language server, it's very easy to get that info.
The point I want to make is that because of this porting Clojure code to Clojurescript implies a lot of inessential changes to the ns forms in your project. E.g: https://github.com/kachayev/muse/blob/8db4d5de82a8acccb4486c..., but I've done far worse.
It doesn't need to be that way.
I agree with your criticism but those "mistakes" sadly come with inheriting implementation details of the hosted environment. As far as I understand, ClojureScript couldn't workaround some JavaScript limitations regarding macros and thus had to go with `:require-macros`.
A workaround is using Reader Conditionals (https://clojure.org/reference/reader#_reader_conditionals) and specifying platform differences where they matter, but it's awkward to say the least. What most projects do is to separate "common" namespaces and use the `.cljc` extension to indicate they're multi platform, and keep platform specific things in namespaces with `.clj`, `.cljs`, etc.
>What most projects do is to separate "common" namespaces and use the `.cljc` extension to indicate they're multi platform, and keep platform specific things in namespaces with `.clj`, `.cljs`, etc.
This is exactly what I witnessed when finding the example above.
Out of frustration, I tried patching shadow-cljs one afternoon and was able to implement :refer :all as well as automatically generating :require-macros when needed to some extent, but I haven't put the time to make it work fully. I don't think this is a limitation caused by the lack of a Clojurescript compiler that can run in a Javascript runtime. In short, I don't think this is an essential limitation of the way the language is hosted within its target language, unlike things like Vars, which are not introspectable at runtime in js.
Oh yeah I definitely lost an hour debugging why something doesn't work in ClojureScript: it turns out I used :refer rather than :refer-macro. It's still possible to use :refer, but it requires some changes to the library such as https://github.com/Engelberg/instaparse/commit/0cd039659dc76...
Clojure hosted in Go: glojure
Go hosted in Clojure: clogo?
How about “clogure” where the “g” is pronounced as in “GIF”?
That made me laugh.
Go hosted in Clojure should be Jo
I like “Glo”
Don't host Go in Clojure.
You're not my dad.
Luke?