clojure design question: why is so much of clojure bootstrapped in clojure.core? i realize that it's cool to build a "base" that is then used to build itself, but given how much of clojure.core still relies on java internals (`list` is defined by calling clojure.lang.PersistentList/.creator, cons is defined by calling clojure.lang.RT/.cons, etc) and likewise the java code relies on the clojure code (macro-expansion, another example i've suddenly forgotten lol), why define stuff like defn or deref or other similar "fundamentals" in clojure.core?
Where would you define them instead?
anywhere else? clojure.lang.RT probably.
I tend to lean on the side of "more should be in core" but that may just be me. 🙂 At some point in a language's life you have to break from implementing core features in the impl lang to bootstrapping them.
anywhere else? clojure.lang.RT probably.How exactly would that work? Like, static initializer inside RT that injects basic functions into clojure.core? I'm not sure i followthere's the special forms like loop* or fn* that are defined in the compiler so the clojure.core var is merely a macro that expands to the special form. and then there are the reader macros which are defined in a combination of lispreader.java, compiler.java, and clojure.core: {} is read in as a call to (apply hash-map (seq (apply concat args))) , unquote is declared in clojure.core but is implemented entirely in lispreader, do is read in as a normal symbol and implemented entirely in compiler.java, and then deref is merely a wrapping macro for clojure.core/deref
not that the vars would be removed from clojure.core, but that more of the var implementations would exist in java code and not clojure code
for many reasons, clojure isn't and won't ever be as fast as java code, so why not implement most of the foundational vars in java?
Thus, have all of those functions as special forms like try and do to be resolved by compiler "by-name" instead of being interned as vars?
> why not implement most of the foundational vars in java?
We don't want to write Java, we want to write clojure
And that leads to a bigger and more complicated compiler for no apparent benefit.
i was thinking closer to like cons, where defn would be implemented like (def defn (fn defn [&form &env name & fdecl] (. clojure.lang.RT (defn &form &env anem & fdecl))))
Something to keep in mind is that a lot of core was written before things like protocols, types, records existed, so the calls down to Java are sometimes reflective of that fact.
yeah for sure
is the question related to speed (since it was mentioned) or separation of concerns and the code being confusing?
uhhh pure curiosity with the various considerations layered on top
I guess there are a lot of whys which are just evolution. I guess as much as the core team put efforts to design upfront, there are going to be a bunch of places where you can't
i was looking at the definition of deref in relation to the discussion of working with Optionals in my #beginners thread, and then thinking about how deref is defined in LispReader as a "WrappingReader" so there's a bit of spiderman pointing at spiderman meme and why not just have deref (a foundational concept given it has a reader macro) implemented in java (instead of having to have deref-future etc)
none of this is a suggestion to change clojure lol, i'm not so foolish
In this particular example, the @ reader delegates to deref the function that is only implemented in clojure.core. There is no Java method that can be used by @ directly.
I understand the desire for Clojure-Java interaction to be purely one-way, but I guess you can never achieve that due to metacircularity
You will always have some Java-defined internal code invoking Clojure-defined code
none of this is a suggestion to change clojure lol, i'm not so foolishI don't want to outright dismiss core changes in general. There are probably many opportunities to improve it, but most of those as mentioned in this thread fall lower on the priorities list.
Also, whenever you replace some Clojure code with a Java method, you lose a lot of development flexibility (can't easily redefine, can't trace, can't monkey-patch etc). So, staying in Clojure land whenever possible is a preferable development experience, and it holds true for developing Clojure itself.
that's a great point i forgot about
just as a follow-up, using @ambrosebs’s wonderful clojure-local-dev set up, i was able to whip up a patch to move deref's impl to RT and it works and changes nothing lol
public static Object derefImpl(Object ref) throws InterruptedException, ExecutionException {
if (ref instanceof clojure.lang.IDeref) {
return ((clojure.lang.IDeref) ref).deref();
} else {
return ((java.util.concurrent.Future) ref).get();
}
}
public static Object derefImpl(Object ref, long timeoutMs, Object timeoutVal) throws InterruptedException, ExecutionException {
if (ref instanceof clojure.lang.IBlockingDeref) {
return ((clojure.lang.IBlockingDeref) ref).deref(timeoutMs, timeoutVal);
} else {
try {
return ((java.util.concurrent.Future) ref).get(timeoutMs, TimeUnit.MILLISECONDS);
} catch (java.util.concurrent.TimeoutException e) {
return timeoutVal;
}
}
}
i suspect there will be very little/no benefit from such a change, but i've satisfied my curiosity that it's at least possible