Fork me on GitHub

How much work is involved to make a Clojure dialect which runs on a non-java runtime (like Clojurescript does)?


is it just map a few dozen primitive operations to the new runtime and fill in the blanks for parts of the standard library that are runtime dependent, or is it much more involved then that?


That's pretty much it, but I think you underestimate how much work just those are


Cljs helps a bit because it already ported some of the standard lib Java code to Clojure


looking at the clojurescript compiler, it seems to be a completely different entity to the clojure compiler, so I guess it's probably fairly involved

Jakub Holý (HolyJak)10:01:11

It was also written much later and leverages learning from the time, i.e. uses protocols heavily (which did not exist when the clojure compiler was written)


it’s a lot of work - check out all the java under the hood


@ludvikgalois What do you want to do?


Nothing serious, I was just hoping to get a (reasonable) subset of clojure running on GNU Guile for a joke. But it looks to be a fairly large amount of work.


For the purposes of the joke, it's much easier to just write "Scheme" with syntax that looks like Clojure and then compile that to Scheme. That only involves needing to write something to parse EDN


there's a racket module that gives clojure features and syntax to scheme, that is probably a better place to start than cljs if you are looking at prior art


I wouldn't call it Clojure without immutable maps, vectors, and sets, and those are all implemented in Java for Clojure. They are implemented in ClojureScript, but that would require implementing deftype and protocols on a new target, at least.


maps, vectors and sets are all HAMTs under the hood, aren't they?


not vectors, but sets are implemented using the map implementations.


Looking at the source for maps and vectors, they seem to be based on the same sort of 32-bit array mapped trie, just with very different insert rules. Vectors aren't implemented as a HAMT, so I guess they're just an AMT? But you're right, it's not a trivial amount of work


I suppose vectors could be considered AMT. I know how vectors and maps are implemented in data structures, and that the map data structures are called HAMTs, but whether AMT is a well-known thing by that name, I do not know


As I mentioned, if you start with ClojureScript's implementation, there those data structures are implemented in the Clojure language itself, so probably easier to port. You need only implement deftype and defprotocol, IIRC


I'm not saying any of that is an insurmountable hurdle, by any means, especially given the existing implementations to go from. Just mentioning some things you definitely want to be sure to implement on a new target.

Jakub Holý (HolyJak)10:01:47

Your irregular reminder - the Exercism Clojure mentoring team is still looking for more mentors. Join us and use few 10s of minutes / month to help a beginner learn Clojure! 🙏

😈 4

Thanks for the heads up - I've just signed up as a mentor. I've had a lot of enjoyment writing Clojure so happy to give back to the community this way at least.

❤️ 8

Thanks for pushing this. I'm currently taking the Clojure track and happy for mentor help

👍 4
Jakub Holý (HolyJak)13:01:03

Thank you, @U067BPAB1! We need all the help we can get 🙂


Hey folks, how can I define limited thread pool in clojure?


thank you very much @U0JEFEZH6 :D


Or use interop


I have a jar file. How can I include that in leiningen as a dependency?


i use the :resource-paths approach for the REBL jar. there are other options discussed here: also, you might have more luck in #leiningen


that jar file is not available on maven


Hey, let’s say I have something like a.clj

(ns a
  (:require [c]))

(defn eval-f
  (eval f))
and b.clj
(ns b
  (:require [a]))

(a/eval-f '(c/some-fn))
Then I’ll get an error like unknown namespace c ie it seems that eval works as if it was called from the b namespace I have done something like
(defn eval-in-ns
  [ns form]
  (binding [*ns* (find-ns ns)]
    (eval form)))
to solve it but I find this behaviour a bit weird and would have thought there would be a with-ns function or something like it to do it (seems that it was in clojure.contrib but has been dropped). Anybody can help me understand why this is the case and why this function doesn’t exist ?


Why not simply require c in b.clj as well, then (a/eval-f c/some-fn)? You can then remove the require from a.clj.


@U067BPAB1 yeah that’s a good point and would work well for this example - however what I was trying to show and that I find a bit confusing is that when calling (a/eval-f f) I would have expected this to be eval’d in the a namespace as this is where eval-f is defined but actually it’s not


That doesn't seem right, is c the actual full name of the namespace or an alias?


plain-quoting a namespaced symbol doesn't resolve ns aliases, no matter what namespace you are in


@UCPS050BV I am not sure I understand what you meant - think it’s the same behaviour whether it’s the full name or an alias - in my case I think it was an alias though eg (ns a (:require [xxxxxx :as c])) ie the namespace is imported where the eval-f is defined - but then when calling eval-f the namespace doesn’t get resolved


my bad, I assumed you had to use syntax quote in order to have aliases resolved


But in any case it would be a huge code smell to write code in namespace a that depended on an alias defined in namespace b


If you used the fullly qualified namespace I believe it would work because c is transitively required


Are there any proper document or source about Strings on Clojure. They are implements serializeable interface but how, why ?


Clojure strings are host strings. Nothing special about them.


I’ll write something about why this call works like smoothly, (map identity "bello!").


But I must be sure about my understanding over it.


Yeah, I know that.


Because Clojure Strings implements Serializeable interface.
*A lot of* false-sources are saying Clojure Strings *are* Sequences, but it is all about interface implementation.
Clojure Strings are just Java Strings.
I think this statement is true ?


You think that strings can have seq called on them because they implement the serialize interface?

Alex Miller (Clojure team)17:01:17

strings are java.lang.Strings

Alex Miller (Clojure team)17:01:31

the Clojure runtime has special case code to treat Strings as "seqable"


This was the piece I was just looking for.


SeqFrom has special casing for CharSequence in RT


I want to dig deep, do you have a link to GitHub file/line ?

dominicm17:01:58 is what you're looking for


Search for "ISeq seq(`

Alex Miller (Clojure team)17:01:42

when you call seq on a string, you get a sequence of characters


Yeah, I know that. But I have to need a proper explanation about it.

Alex Miller (Clojure team)17:01:11

serializable has nothing to do with it

Alex Miller (Clojure team)17:01:09

I don't know of any reference documentation about it


This will suffice for the explanation, thank you guys. But I’m very curious person. I need to dig about it.


Basically, Clojure has the concept of Seqable. Those are things that can be coerced to a Seq using the seq fn


And Strings are seqable


Thus they can be coerced to a Seq with seq


Thus all functions that take a seqable will work on Strings as well


seq function has some documentation on it. Not about implementation, just about the contract.


If you want to dig, you will want to dig into Clojure's implementation code quite a bit, not only the part written in Clojure, but also the part written in Java. Feel free to ask for pointers into that source code if you can't find what you are looking for.


Ok, this special treatment comes from Clojure Run-Time. But, are there any special situations like this ?


What do you mean? (seq "").


Or do you mean any other special cases?


Anything else.


Just search for instanceof in the file. :)

parrot 8

I’m doing it. 🙂


BTW, is considered implementation detail? I.e. does it need to preserve backwards compatibility in terms of its interfaces?


Nice question.


I think the general answer is if it's not in the documentation under "reference" or API on then it may change


I started to wonder because I saw clojure.lang.RT#assocN with a comment that says: "//hmm... this is not persistent". But at the same time, this static method is not used anywhere (well, except if there's some obscure reflection going on).

Alex Miller (Clojure team)18:01:39

"is considered implementation detail?" yes "does it need to preserve backwards compatibility in terms of its interfaces" yes

Alex Miller (Clojure team)18:01:12

AOT compiled Clojure includes calls into RT and thus we try pretty hard not to break binary compatibility with the RT interface (or Clojure code compiled on an old version would not run on a new version). I actually can't think of a bug report or complaint like this in my memory so I think we do a pretty good job at that.

Alex Miller (Clojure team)18:01:55

you may find some vestigial methods in RT that are no longer used but have been preserved for compatibility


Oh, so ABI is still relevant here. After halting any serious work done in C++ I kinda stopped having this problem and eventually forgot about it completely. I see, thanks.

Alex Miller (Clojure team)18:01:23

most Clojure code is distributed and consumed as source (so late-compiled when used) so it's usually not something to worry about much

Alex Miller (Clojure team)18:01:42

Clojure core is a bit special in being a mix of Clojure and Java and mostly AOT compiled

Drew Verlee18:01:48

Is it possible to get meta information about a function at run time? I captured the var in a atom, derefing it tells me the fn sig (in emacs with cider). But i'm curious if you can get more information at run time. I would link so, but my first pass (meta @foo) didnt work.

Alex Miller (Clojure team)18:01:17

from a var or a function instance?

Alex Miller (Clojure team)18:01:51

for the former, meta should tell you things. for the latter, no it's opaque (just an invokable thing)

Alex Miller (Clojure team)18:01:37

"capture the var" is unclear to me which one you did


(meta @foo) is pulling the metadata from the fn


(meta #'foo) pulls the metadata from the var

Drew Verlee19:01:51

Just talking about terminology. What would describe this:

(defn foo []) 
(def d (atom nil)
(defn zoo [x] (reset! d x))
(zoo foo)
I would say i set the value of the atom to the function foo. Though "function" is more accuratly a "Var". right? I haven't thought about this in a while. Thats what i was calling capturing. I had hopped to then deref that atom value and get meta information on the function. i assume a docstring is meta information. I'm not sure what all could be there.


the var foo will be dereferenced here and the value inside the atom will be the function object that the var foo points to


you could reassign a new object to the var foo and the object inside the atom would not change


the useful meta information for a defined function is stored on the var, not on the function object


if you wanna store the var foo in the atom you could do (zoo #'foo)


from the var you can get the meta information as well as the function object

Drew Verlee19:01:58

Ah, good point. I was thinking the information was on the function object not the var.


user=> (defn foo [x] (println x))
user=> (meta foo)
user=> (meta #'foo)
{:arglists ([x]), :line 1, :column 1, :file "NO_SOURCE_PATH", :name foo, :ns #object[clojure.lang.Namespace 0x14e2e1c3 "user"]}


user=> @#'foo
#object[user$foo 0x60e21209 "user$foo@60e21209"]
user=> (@#'foo "hey!")


(defn foo [x]
  (println x))

 (class foo)
 (class @#'foo))
;; => true

(fn? foo)
;;=> true


for the record this is what ended up working to publish 2 jars with different deps from a single project

:aliases {"deploy!" ["with-profile" "default:fixture" "deploy"]}
  :profiles {:test-deps {:dependencies [[org.testcontainers/testcontainers "1.12.3"]
                                        [org.testcontainers/postgresql "1.12.3"]]}
             ;when we run `lein with-profile fixture install` (or `lein deploy!`), gr-db/gr-db-fixture-{version}.jar is published with the test-deps included in the pom.xml
             :fixture ^:leaky [:test-deps {:source-paths ^:replace ["src-helper"]
                                           :name         "gr-db-fixture"}]
             :dev [:test-deps {:resource-paths ["test-resources"]
                               :source-paths   ["src-helper"]
                               :dependencies   [[ch.qos.logback/logback-classic "1.2.3"]]}]}


note re: the comment, about test-deps being included, that profile actually excluses the normal "src" tree (it would merge in src-helper with src if the ^:replace metadata wasn't there)


because the default for that setting is to merge the vectors and not replace them, and the vector contains "src" by default, but the ^:replace metadata tells lein to remove the default setting