Fork me on GitHub
#clojure
<
2020-01-06
>
ludvikgalois09:01:46

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

ludvikgalois09:01:09

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?

didibus21:01:57

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

didibus21:01:15

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

ludvikgalois09:01:32

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

ludvikgalois09:01:32

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

holyjak10: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)

dazld09:01:41

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

borkdude10:01:40

@ludvikgalois What do you want to do?

ludvikgalois10:01:35

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.

ludvikgalois10:01:25

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

noisesmith19:01:24

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

andy.fingerhut10:01:58

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.

ludvikgalois10:01:04

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

andy.fingerhut10:01:58

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

ludvikgalois11:01:59

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

andy.fingerhut20:01:24

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

andy.fingerhut20:01:22

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

andy.fingerhut10:01:13

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.

holyjak10: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! https://exercism.io/become-a-mentor 🙏

sgerguri11:01:51

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.

kamuela11:01:52

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

holyjak13:01:03

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

quieterkali14:01:15

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

quieterkali19:01:39

thank you very much @ :D

didibus21:01:51

Or use interop

i14:01:40

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

sogaiu16:01:17

i use the :resource-paths approach for the REBL jar. there are other options discussed here: https://stackoverflow.com/questions/2404426/leiningen-how-to-add-dependencies-for-local-jars also, you might have more luck in #leiningen

i14:01:51

that jar file is not available on maven

thomas.ormezzano14:01:37

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

(ns a
  (:require [c]))

(defn eval-f
  [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 https://clojure.github.io/clojure-contrib/branch-master/with-ns-api.html but has been dropped). Anybody can help me understand why this is the case and why this function doesn’t exist ?

sgerguri14:01:56

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.

thomas.ormezzano15:01:55

@ 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

qythium15:01:56

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

qythium15:01:57

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

thomas.ormezzano16:01:31

@ 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

qythium16:01:45

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

qythium16:01:15

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

qythium16:01:32

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

scknkkrer17:01:34

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

dominicm17:01:14

Clojure strings are host strings. Nothing special about them.

scknkkrer17:01:22

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

scknkkrer17:01:50

But I must be sure about my understanding over it.

scknkkrer17:01:53

Yeah, I know that.

scknkkrer17:01:55

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 ?

dominicm17:01:48

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

alexmiller17:01:17

strings are java.lang.Strings

alexmiller17:01:31

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

dominicm17:01:17

This was the piece I was just looking for.

dominicm17:01:12

SeqFrom has special casing for CharSequence in RT

scknkkrer17:01:07

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

dominicm17:01:58

RT.java is what you're looking for

dominicm17:01:15

Search for "ISeq seq(`

alexmiller17:01:42

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

scknkkrer17:01:52

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

alexmiller17:01:02

that's the whole story

alexmiller17:01:11

serializable has nothing to do with it

alexmiller17:01:09

I don't know of any reference documentation about it

scknkkrer17:01:40

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

didibus21:01:41

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

didibus21:01:19

And Strings are seqable

didibus21:01:38

Thus they can be coerced to a Seq with seq

didibus21:01:02

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

dominicm17:01:45

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

andy.fingerhut20:01:02

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.

scknkkrer17:01:41

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

p-himik17:01:55

What do you mean? (seq "").

p-himik17:01:11

Or do you mean any other special cases?

scknkkrer18:01:56

Anything else.

p-himik18:01:36

Just search for instanceof in the RT.java file. :)

scknkkrer18:01:55

I’m doing it. 🙂

p-himik18:01:07

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

scknkkrer18:01:33

Nice question.

bfabry18:01:26

I think the general answer is if it's not in the documentation under "reference" or API on http://clojure.org then it may change

p-himik18:01:12

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).

alexmiller18:01:39

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

alexmiller18: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.

alexmiller18:01:55

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

p-himik18:01:32

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.

alexmiller18: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

alexmiller18:01:42

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

drewverlee18: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.

alexmiller18:01:17

from a var or a function instance?

alexmiller18:01:51

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

alexmiller18:01:37

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

alexbaranosky19:01:18

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

alexbaranosky19:01:30

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

drewverlee19: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.

drewverlee19: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.

bfabry19:01:55

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

bfabry19:01:14

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

bfabry19:01:53

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

bfabry19:01:10

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

bfabry19:01:25

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

drewverlee19:01:58

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

bfabry19:01:35

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

bfabry19:01:09

user=> @#'foo
#object[user$foo 0x60e21209 "[email protected]"]
user=> (@#'foo "hey!")
hey!
nil

alexbaranosky19:01:38

(defn foo [x]
  (println x))

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

(fn? foo)
;;=> true

antonmos20:01:42

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"]]}]}

noisesmith20:01:31

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)

noisesmith21:01:36

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