This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-05
Channels
- # announcements (15)
- # aws (7)
- # babashka (105)
- # beginners (35)
- # biff (5)
- # calva (48)
- # cider (5)
- # clj-kondo (25)
- # cljdoc (14)
- # clojure (84)
- # clojure-czech (2)
- # clojure-dev (6)
- # clojure-europe (58)
- # clojure-nl (6)
- # clojure-norway (19)
- # clojure-portugal (2)
- # clojure-uk (5)
- # clojurescript (23)
- # cloverage (5)
- # code-reviews (5)
- # conjure (28)
- # data-science (1)
- # datomic (53)
- # events (6)
- # exercism (7)
- # fulcro (16)
- # graalvm-mobile (2)
- # honeysql (29)
- # improve-getting-started (2)
- # kaocha (32)
- # lambdaisland (2)
- # lsp (29)
- # malli (3)
- # overtone (1)
- # pedestal (8)
- # polylith (3)
- # portal (6)
- # quil (2)
- # rdf (15)
- # releases (2)
- # rewrite-clj (14)
- # sci (9)
- # shadow-cljs (7)
- # specter (5)
- # sql (5)
- # xtdb (38)
are there any clojure learning materials for kids? I've got a 9 and 13 year old that it's basically time to get these kids programming.. I've been using Clojure for the Brave and the Bold as a good starter, along with gorilla repl as their workspace
I'll look into night code, Zach Oaks used it as a teaching environment for children, it's interactive and they can get their hands dirty by modifying a game and tinkering instead of pedagogically learning to program More fun and engaging https://github.com/oakes/Nightcode
There are beginner friendly learning paths at https://clojurebridgelondon.github.io/
I'm expecting "no" but is it possible to do something like this?
(reify java.util.Iterator (remove [this] (if-let [m (get methods 'remove)] (m this) (invoke-default-method this))))
Where methods is a map of symbol to function and if not found, invoke the default method from the interface. How does one get invoke-default-method
, if at all possible?This is still invoking the provided implementation:
user=> (let [m (first (.getMethods java.util.Iterator))] (.invoke m (reify java.util.Iterator (remove [this] (prn :hello))) nil))
:hello
nil
(.invoke (->> "java.util.Iterator" Class/forName .getMethods (filter #(= (.getName %) "remove")) first) (reify java.util.Iterator) (into-array Object []))
@U11SJ6Q0K It should work with reified objects that have already an implementation for remove
so could bb reify automatically add any missing methods with default invocations? idk how well graalvm reflection works
ah, but there was already some master object, that implemented all the interfaces in bb
No, you can't do that :/ The way it works now is: there is a reified implementation which dispatches dynamically, but there's already a 'remove
method.
Yes, indeed.
Something like this:
(def method-handle (.findSpecial java.util.Iterator "remove" (java.lang.invoke.MethodType/methodType nil)))
And then:
(let [m (first (.getMethods java.util.Iterator))] (let [o (reify java.util.Iterator (remove [this] (prn :hello)))] (let [m (.bindTo method-handle o)] (.invokeWithArguments m))))
A little closer:
(def method-handle (.findSpecial lookup java.util.Iterator "remove" (java.lang.invoke.MethodType/methodType (Void/TYPE)) java.util.Iterator))
But now:
user=> (let [o (reify java.util.Iterator (remove [this] (prn :hello)))] (.invoke ^java.lang.invoke.MethodHandle method-handle o))
Execution error (ClassCastException) at user/eval326 (REPL:1).
class user$eval326$reify__327 cannot be cast to class [Ljava.lang.Object; (user$eval326$reify__327 is in unnamed module of loader clojure.lang.DynamicClassLoader @14e2e1c3; [Ljava.lang.Object; is in module java.base of loader 'bootstrap')
user=> (let [o (reify java.util.Iterator (remove [this] (prn :hello)))] (let [m (.bindTo method-handle o)] (.invoke ^java.lang.invoke.MethodHandle m (into-array []))))
Execution error (WrongMethodTypeException) at java.lang.invoke.MethodHandle/asTypeUncached (MethodHandle.java:861).
cannot convert MethodHandle()void to (Object[])Object
:thinking_face:(java.lang.invoke.MethodType/methodType (Void/TYPE))
Perhaps? At least seems to work in my REPL.Bingo!
user=> (def method-handle (.findSpecial lookup java.util.Iterator "remove" (java.lang.invoke.MethodType/methodType (Void/TYPE)) java.util.Iterator))
#'user/method-handle
user=> method-handle
#object[java.lang.invoke.DirectMethodHandle$Special 0x29c5ee1d "MethodHandle(Iterator)void"]
user=>
Any ideas one this one?
(let [o (reify java.util.Iterator (remove [this] (prn :hello)))] (let [m (.bindTo method-handle o)] (.invoke ^java.lang.invoke.MethodHandle m (into-array []))))
Execution error (WrongMethodTypeException) at java.lang.invoke.MethodHandle/asTypeUncached (MethodHandle.java:861).
cannot convert MethodHandle()void to (Object[])Object
The goal is to execute the default method of the interface - not the overridden method
I’m not sure if that’s possible from outside the object, you’d need to get the super
reference within for that.
I'm afraid that will still invoke the virtual method, which I think can be prevented with findSpecial, but not sure
There’s https://clojuredocs.org/clojure.core/proxy-super which seems sort of related but I’ve never touched this part of Clojure so dunno really… Reflection sort of works, but yeah, the virtual method call goes to the instance which follows the encapsulation rules. You could define an additional method for getting the super instance and call it from there, but that’d be quite hacky.
And I can’t guarantee that would work either 🙂 Although my memory is getting fuzzy on all things Java, have had a 5 year pause at it at this point…
For a direct call, you really need to be in a direct subclass and I think proxy is your only path on Clojure to call the super. For reflection, I think the key is you’d want super to be the target and not sure there is any way to do that. Maybe back out to the actual problem?
Sure. The actual problem is that I need to construct a reified Object at compile time that dispatches on functions dynamically, so I have to already implement those methods before-hand
But when someone doesn't provide an implementation, I want to call to the default method.
Perhaps I can make the compile time object using proxy, that would be an interesting option
Do you actually mean an interface default method?
Isn't the whole point that you don't explicitly do anything? If the method is absent you should get the default
Yes, but the problem is that in SCI you don't know when the method is absent, unless you pre-generate all permutations (which leads to a big image size). So it has to provide some object with all methods already in place
The idea is: one compile time object, which dispatches for all runtime (interpreted) representations of that object
In Java, you can make this explicit call with TheInterface.super.theMethod(…) but there is no such syntax to say that in Clojure
Maybe it's possible to do with reflection, have never tried that
I tried this using MethodHandles but when you have the methodhandle it still invokes the "virtual" method. I read something about findSpecial
which may provide a workaround for this
This is what I got so far (not working yet):
(def method-handle (.findSpecial lookup java.util.Iterator "remove" (java.lang.invoke.MethodType/methodType (Void/TYPE)) java.util.Iterator))
user=> (let [o (reify java.util.Iterator (remove [this] (prn :hello)))] (let [m (.bindTo method-handle o)] (.invoke ^java.lang.invoke.MethodHandle m (into-array []))))
Execution error (WrongMethodTypeException) at java.lang.invoke.MethodHandle/asTypeUncached (MethodHandle.java:861).
cannot convert MethodHandle()void to (Object[])Object
Clojure doesn't know what to do with signature polymorphic calls, like MethodHandle.invoke* and VarHandle.*
@U050ECB92 Good to know. But in this case, there is only one overload of remove
right?
yes, it found the correct call (when you grab a MethodHandle it is already narrowed down to a single target)
but when it is trying to invoke it, it's trying to invoke it in an Object returning context
> When the JVM processes bytecode containing signature polymorphic calls, it will successfully link any such call, regardless of its symbolic type descriptor.
@U050ECB92 Is there any way to do this in Clojure, or should I revert to Java for this?
@U050ECB92 In SCI I cannot use reify
to implement reify at runtime. So I need to create one reified object at compile time which dynamically dispatches to a map of implementations at runtime. But if a method is not provided in that map, I need to call the default method.
Gotta say, writing code against invoke with https://github.com/jgpc42/insn is actually really pleasant, and you can do nice things like define clojure ifns that have bytecode bodies like
(bc/fn something ^long [^long n]
[[:lload 1]
[:ldc2 1]
[:ladd]
[:lreturn]])
Maybe I'll just automate the .java class generation though and compile into an artifact
Ended up going with the bytecode approach using insn. Thanks for the suggestion @U5NCUG8NR!
Hi eveyone \o Hope you're all doing well.
Does anyone know where can I find documentation about clj -Ttools
? I tried clj -Ttools --help
, clj -Ttools --help
but got errors. I tried to google it too but don't found anything.
There is description and examples of Clojure CLI execution options at https://practical.li/blog/posts/clojure-which-execution-option-to-use/ Includes examples of using -T as an installed tool and with aliases
Thanks! It's really great!
Is there a particular reason that the forkjoin pool and fjtask are public in reducers but fjinvoke, fjfork, and jfjoin are private?
It seems to me that it'd be useful to have all of them be public for people who wish to implement CollFold in libraries, like I'd like to do with my rope implementation.
don't know off-hand - if you have a concrete request, interested in a better problem statement at https://ask.clojure.org - are you looking to do something like what the PHM extension does?
I'm not familiar with what the PHM extension is. The concrete example of what I want to do is just implement CollFold for a rope, which is a tree structure for constant time concatenation in the average case of long sequences, mostly used in text editors. Since these tend to be very large structures, having CollFold could help a lot with performance sensitive things. I can throw something on the website later today.
I ran into this when I implemented fold for next.jdbc
and just copied them into next.jdbc.result-set
as private functions.
;; ForkJoinTask wrappers copied in from clojure.core.reducers to avoid
;; relying on private functionality that might possibly change over time
(defn- fjtask [^Callable f]
(ForkJoinTask/adapt f))
(defn- fjinvoke
"For now, this still relies on clojure.core.reducers/pool which is
public but undocumented."
[f]
(if (ForkJoinTask/inForkJoinPool)
(f)
(.invoke ^ForkJoinPool @r/pool ^ForkJoinTask (fjtask f))))
(defn- fjfork [task] (.fork ^ForkJoinTask task))
(defn- fjjoin [task] (.join ^ForkJoinTask task))
Yeah, I'm considering doing the same.
The direct use of @r/pool
bothers me, but the other functions are "trivial" and don't feel like they need to be part of the reducers
API...?
Hey, given that core/assert-args
macro is private, but really useful, do you see any problem in copying it to our projects, into a util
or misc
namespace?
Does anyone have a pointer to a library/program that figures out stats on Clojure program files? I'm thinking of # of functions/macros, LoC with/without comments, etc. I figure if I had do, I could spin my own crappy version, but there's no sense in re-inventing stuff that already exists and, if there is something out there, it probably deals better with corner cases in the language than I have time to fool around with.
The venerable cloc.pl (I think its page is http://cloc.sourceforge.net/) detects and counts Clojure. cloc.pl has no Clojure-specific metrics, but if you are counting heterogeneous stuff like a project that also includes Java, XML, XSL, CSS, bash, etc etc etc, it is quite convenient.
I can't say if such a tool already exists, but I can say that such a tool could almost trivially be implemented atop #clj-kondo, so maybe head over there and ask about the analysis output if you can't find anything that meets your needs.
There's also https://github.com/jpmonettas/clindex where you can write arbitrary queries about a codebase