Fork me on GitHub
#clojure
<
2021-10-10
>
indy10:10:33

The following throws a syntax error (which was a little unexpected) because as I understand, the interning of keywords happens at the read phase and the ns-alias cannot be resolved here.

#_::non-existent-ns-alias/hello
Just trying to learn and understand if this ns resolution for keyword interning should ideally happen at the read or compile phase? And if my feeling of unexpectedness is justified?

Alex Miller (Clojure team)12:10:07

#_ will read the next thing then skip. The :: is resolved at read time so it fails before it can be skipped.

Alex Miller (Clojure team)12:10:32

That said, we actually have a ticket to maybe make this possible

đź‘Ś 1
octahedrion11:10:41

I'm trying to compile the longer example of gen-class in https://clojure.org/reference/compilation#_gen_class_examples with https://github.com/clojure/tools.build but I can't get it to compile with compile-clj, it throws

Syntax error (ClassNotFoundException) compiling new at (clojure.examples.instance.clj:41:11). 
clojure.examples.instance
whereas I can compile it with (compile 'clojure.examples.instance) ( classes is in included in the classpath in both cases)

Alex Miller (Clojure team)12:10:31

Is your file named clojure.examples.instance.clj? That should really be at the path clojure/examples/instance.clj. What does new refer to in your code? (That's not part of the referenced example)

octahedrion13:10:35

the namespace clojure.examples.instance is in a file at src/clojure/examples/instance.clj and I have :paths ["src"] in my deps.edn and :extra-paths ["classes" "target/classes"] in the :build alias. The line the exception refers to is line 41 (let [x (new clojure.examples.instance s)... here's the REPL output:

(compile 'clojure.examples.instance)
=> clojure.examples.instance ; works

(b/compile-clj {:ns-compile '[clojure.examples.instance] :basis (b/create-basis {:project "deps.edn"}) :class-dir "target/classes"})    ; :class-dir "classes" gives same error
Syntax error (ClassNotFoundException) compiling new at (clojure/examples/instance.clj:41:11).
clojure.examples.instance
and the full report says:
{:clojure.main/message "Syntax error (ClassNotFoundException) compiling new at (clojure/examples/instance.clj:41:11).\nclojure.examples.instance\n", :clojure.main/triage {:clojure.error/phase :compile-syntax-check,  :clojure.error/line 41,  :clojure.error/column 11,  :clojure.error/source "instance.clj",  :clojure.error/symbol new,  :clojure.error/path "clojure/examples/instance.clj",  :clojure.error/class java.lang.ClassNotFoundException,  :clojure.error/cause "clojure.examples.instance"}

Alex Miller (Clojure team)04:10:31

I've added a fix for TBUILD-20 in v0.6.0 of tools.build (sha b139316), give it a try

octahedrion07:10:48

confirmed it works now, thank you @U064X3EF3 !

Alex Miller (Clojure team)14:10:40

I think you may be running into https://clojure.atlassian.net/browse/TBUILD-20 which is a bug I'm going to look at today

octahedrion15:10:30

yep - it works when I update the basis as described - thanks!

zhuxun216:10:35

When using deftype, where to put the one-time expensive processing of the constructor arguments? Let's say I'm making my own Hash Map abstraction:

(deftype HashMap [items]
  clojure.lang.IPersistentMap
  (assoc ...)
  (assocEx ...)
  (without ...)
  (count ...)
  ...
)
Where should I put the code of actually building the internal data structure, so that all other methods will have access to it? Or is deftype not the macro to do this?

zhuxun216:10:22

In other word, is there a place to put a constructor function in deftype?

zhuxun216:10:50

Okay, looks like all these answers suggest this is impossible to do in Clojure and I'd be better off writing Java for building a class https://stackoverflow.com/questions/6515162/add-constructor-to-deftype-created-class

zhuxun216:10:19

I see that @seancorfield used the approach of extracting out the constructors as outside functions, e.g.:

(defrecord HashMap [expensive-data-structure]
  clojure.lang.IPersistentMap
  (assoc ...)
  (assocEx ...)
  (without ...)
  (count ...)
  ...
)

(defn make-hash-map [items]
  (let [expensive-data-structure <...expensive code..>]
    (->HashMap expensive-data-structure))
and make-hash-map can be used as the de-facto constructor.

zhuxun216:10:20

But I guess HashMap is a bad example here because some of its methods (such as assoc) is best implemented with mutations to the expensive-data-structure, which is going to be quite awkward to do in Clojure. However, I can see that this "external constructor" technique being used on immutable records that need some one-time processing for constructing arguments -- e.g., a read-only HashMap.

Joshua Suskalo18:10:53

The advice from 2014 is that we shouldn't use :inline. It hasn't changed in the meantime, is that still true? At the moment I'm writing a wrapper for Project Panama (it'll be ready for proper release soon), and in order to keep performance good for primitive types even when serializers and deserializers are needed for composite types I have to avoid calling the serde multimethods for primitive types. The best case here is that I expand it with inline to reduce the runtime cost. Is this inadvisable?

borkdude18:10:29

You've probably already thought of this, but is it possible to do it with a macro?

Joshua Suskalo18:10:56

Yes, I definitely could, and for some of it I will be doing just that, but I need them to also operate as functions, even if that means it takes a slight performance hit.

Joshua Suskalo18:10:08

I'd prefer to not need to expose two different vars for this.

borkdude18:10:04

CLJS has a neat solution for this: macro-fns

Joshua Suskalo18:10:20

I'll admit that I'm fighting for ns, but the cost per argument is ~50ns, even for primitives, and Panama is screaming fast so that completely dominates execution time.

Ben Sless18:10:04

you can use definline

Joshua Suskalo18:10:33

No, definline is an exclusively inline function.

Joshua Suskalo18:10:53

This needs both

Joshua Suskalo18:10:58

also that's still experimental, which is what prompted the question in the first place.

Ben Sless18:10:09

Maybe I misunderstood, besides :inline being experimental does it not do what you needed? I also tried poking at the issue of :inline's status several times, it's been experimental for years. I wouldn't feel bad relying on it, already do in my own library, and I suppose even if it will be deprecated it will be in favor of a better solution and we'll have a migration path, too

Joshua Suskalo18:10:34

Well it's never been standardized, so likely if it's changed it would be deprecated, nor is an upgrade path guaranteed. That's why I'm asking here. If this is the status though, I'm likely to just make the version with functions be slower than the one with macros, since the macro case is the more common usecase anyway.

Ben Sless18:10:01

Theoretically, Clojure has never been standardized. Contrast with EDN which has been. Leaves us a bit of wriggle room, I believe.

Alex Miller (Clojure team)19:10:38

inline may eventually be replaced with something else, but I think you can use it now if you understand that caveat

Ben Sless19:10:56

Are there any plans you're at liberty to share?

Alex Miller (Clojure team)20:10:40

There are no active plans

đź‘Ť 2
Joshua Suskalo21:10:13

Alright, thanks, that's helpful.

didibus04:10:37

Were there ideas of alternative ways to offer something similar to :inline ? Is that why it "may be replaced by something else", because the core team discussed alternatives or feel like there should be something better to it?

slipset12:10:54

A wise man once said something along the lines that the future is both hard to predict and very long. So “may be replaced by something else” is in this case a rather weak statement, almost a tautology, but rules out the much stronger statement “will never be replaced by something else”.

Alex Miller (Clojure team)13:10:46

Rich does have some ideas about this but we haven't talked about it in a long time