5 ms·
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.
by geokon 5y ago
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 syntactic sugar to make it more light weight like Clojure?
With hooking into GDB you could probably make a REPL as well.. GDB has live code reload, introspection - you even get state of crash and sane stack traces :)
- vnorilo 5y agoYou 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.
- yawaramin 5y agoCheck out https://github.com/carp-lang/Carp https://github.com/carp-lang/Carp From the readme: > The key features of Carp are the following: > - Automatic and deterministic memory management (no garbage collector or VM) > - Inferred static types for great speed and reliability > - Ownership tracking enables a functional programming style while still using mutation of cache-friendly data structures under the hood > - No hidden performance penalties – allocation and copying are explicit > - Straightforward integration with existing C code > - Lisp macros, compile time scripting and a helpful REPL
- geokon 5y agoI've never had to use it myself, but what part of the JNI/JNA is problematic? One thing I'd sorta fantasized about is (in a very hand wavey way) using Clojure + ClojureCL to run OpenCL kernels. In theory if you were to bundled your JAR with POCL.. you could selectively run your tight-loops/kernels on the CPU or GPU. So it'd be effectively like launching little C snippets. Furthermore, from what I remember, kernels are compiled and executed at run time - so it'd really fit with the whole REPL workflow and they would effectively simply be inline strings of C code that get executed I think the main catch for the moment is that ClojureCL uses a MKL matrices to talk to the OpenCL driver (driver? dispatcher? I might be mixing up my terms) - but that could be modified to a JVM container Probably my own bias - but the only time I've really been let down by Clojure and missed native was when I need to optimize a tight loop. Once you optimized Clojure past a certain point (to eliminate reflection, boxing issues, destructuring etc.) it starts to get ugly.
- didibus 5y agoInterop 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!
- geokon 5y agoIt does already sort of exist: https://ferret-lang.org/ https://ferret-lang.org/
- aidenn0 5y agoConsider perhaps https://github.com/clasp-developers/clasp https://github.com/clasp-developers/clasp which is a common-lisp specifically designed to interop with C++.
- iainctduncan 5y agoYou could check out s7 Scheme. I'm using it for music apps, and it shares a lot of Clojure's design decisions, like being fundamentally a lisp-1 but having many CL inspired features. It's very minimal, and super easy to embed in a C/C++ host. And liberally licensed.
- dig1 5y ago> 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 allocate more memory than there are actually elements in it, and you never know when it will start resizing it (this is implementation detail). I would not call this as "zero cost abstraction". [1] https://clojuredocs.org/clojure.core/seq https://clojuredocs.org/clojure.core/seq
- vnorilo 5y agoI think zero cost in this context means compile-time polymorphism (templates) instead of runtime polymorphism (virtual functions).
- geokon 5y agoI 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