Fork me on GitHub
#clojure
<
2024-06-18
>
oskarkv03:06:20

I have a problem I am stumped by. I have a macro that I can expand.

game.cljgl.core> (macroexpand '(define-gl-stuff))

(do
 (clojure.core/defn raster-pos3fv ([a0] (GL33/glRasterPos3fv a0)))
 (clojure.core/alter-meta!
  #'raster-pos3fv
  game.cljgl.core/set-arglists
  '([float<>] [java.nio.FloatBuffer])))
And I can take that expansion and copy it into the repl, and it works as expected, without any error. But when I run the macro normally, I get an error:
game.cljgl.core> (define-gl-stuff)

Execution error (IllegalArgumentException) at game.cljgl.core$eval15668/ (REPL:13).
No matching ctor found for class clojure.reflect$typesym$fn__12067
Does anyone have any ideas of what is wrong?

oskarkv03:06:56

Stack trace from calling define-gl-stuff normally:

Reflector.java:  305  clojure.lang.Reflector/invokeConstructor
           LispReader.java: 1317  clojure.lang.LispReader$EvalReader/invoke
           LispReader.java:  853  clojure.lang.LispReader$DispatchReader/invoke
           LispReader.java:  285  clojure.lang.LispReader/read
           LispReader.java:  216  clojure.lang.LispReader/read
           LispReader.java:  205  clojure.lang.LispReader/read
                   RT.java: 1876  clojure.lang.RT/readString
                   RT.java: 1871  clojure.lang.RT/readString
                      REPL:   48  game.cljgl.core/eval15707
               Unsafe.java:   -2  jdk.internal.misc.Unsafe/ensureClassInitialized0
               Unsafe.java: 1160  jdk.internal.misc.Unsafe/ensureClassInitialized
MethodHandleAccessorFactory.java:  300  jdk.internal.reflect.MethodHandleAccessorFactory/ensureClassInitialized
MethodHandleAccessorFactory.java:  103  jdk.internal.reflect.MethodHandleAccessorFactory/newConstructorAccessor
    ReflectionFactory.java:  200  jdk.internal.reflect.ReflectionFactory/newConstructorAccessor
          Constructor.java:  549  java.lang.reflect.Constructor/acquireConstructorAccessor
          Constructor.java:  499  java.lang.reflect.Constructor/newInstanceWithCaller
          Constructor.java:  486  java.lang.reflect.Constructor/newInstance
             Compiler.java: 5008  clojure.lang.Compiler$ObjExpr/eval
             Compiler.java: 7193  clojure.lang.Compiler/eval
             Compiler.java: 7183  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3216  clojure.core/eval
                  core.clj: 3212  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java: 1583  java.lang.Thread/run

hiredman03:06:47

Your macro expansion contains a closure object(a fn object) instead of code that when evaluated creates it

hiredman03:06:29

That error is the result of the compiler being unable to embedded the closure in the emitted bytecode

oskarkv03:06:27

I don't quite understand. How come I can't see the closure in the output of macroexpand?

oskarkv03:06:31

I found out that if I remove the arglist (`([float<>] [java.nio.FloatBuffer])`) and replace it with an empty list then it works. So, a closure is hiding in there?

hiredman03:06:12

Likely, looks like you are getting them from the output of clojure.reflect, which is where the object that is not being serialized in the bytecode correctly is coming from

hiredman03:06:18

It is in the metadata on the symbols you get back from clojure.reflect

oskarkv03:06:05

Thanks a lot!

m3tti06:06:55

Hey any ruuter users? How can i do a redirect with ruuter? can't find anything 😕

chucklehead06:06:36

Return a map with a 30x status code and the Location header set to the URI you want to redirect to, e.g.:

{:status 301 :headers {"Location" "/go/somewhere-else"}}

jasonjckn08:06:15

I always thought every transduce could be rewritten with reducers, but it seems like the completing function isn't called, which makes sense in retrospect given the protocols involved. Kind of disappointed now, because this was my way of writing transducer code that still looked like lazy seq code... Is there any way to salvage this, and write a partition-all reducer?

erwinrooijakkers09:06:00

https://github.com/clojure/clojure/blob/clojure-1.11.1/src/clj/clojure/core.clj#L7308 is a stateful transducer, at the end in the completion step the remaining state is obtained and returned (the last partition). For your reducer to work you do need this final step I guess.

jasonjckn09:06:02

yah, i guess i'm trying to find a way to get reducers to actually invoke that final step

erwinrooijakkers09:06:22

you can call it yourself

jasonjckn09:06:40

I sort of got it to do it,

(defn completing-reducible [coll]
       (reify
         clojure.core.protocols/CollReduce
         (coll-reduce [this f1]
           (clojure.core.protocols/coll-reduce this f1 (f1)))
         (coll-reduce [_ f1 init]
           (f1
             (clojure.core.protocols/coll-reduce coll f1 init)))))

      (->>  (r/reducer (completing-reducible (range 10) )
                      (partition-all 3))
           (transduce identity conj [])) 

👍 1
jasonjckn09:06:10

i want to keep the lazy-seq-like syntax, of reducers, and have reducers like partition-all.

(->> (range 10)
        (r/map inc)
        ...
        (into [])
that all works until you have transducer that has the final step, like partition-all. there's no easy way to write
(->> (range 10)
        (r/map inc)
        (r.x/partition-all 2)
        ...
        (into [])

jasonjckn09:06:15

This partly explains why clojure.core.reducers is missing so many functions

jasonjckn09:06:04

I arrived where I wanted to it terms of lazy seq syntax, but i can't tell yet if this implementation is sound

(defn reducer*
       ([coll xf]
        (reify
          clojure.core.protocols/CollReduce
          (coll-reduce [this f1]
            (clojure.core.protocols/coll-reduce this f1 (f1)))
          (coll-reduce [_ f1 init]
            (let [rf (xf f1)]
              (rf 
                (clojure.core.protocols/coll-reduce coll rf init)))))))

     (defn partition-all* [n coll]
       (reducer* coll (partition-all n)))

     
     (->>  (range 10 )
           (partition-all* 3)
           (transduce identity conj [])) ; ;=> [[0 1 2] [3 4 5] [6 7 8] [9]]
EDIT: yup, not accurate, completing function may get invoked multiple times. I guess i'll just write them idempotently, this as good as it gets I think.

exitsandman09:06:27

my personal tip is to just embrace the natural shape of transducer composition

👍 1
jasonjckn09:06:11

yah.. i just can't help but think there's a nice solution here. when you're in the REPL its not uncommon to eval things to see what they look like before its reduced.

p-himik09:06:17

How would you do it with the (->> ...) syntax? Wouldn't you have to comment out the whole last form?

jasonjckn09:06:52

@U2FRKM4TW are you asking about this ?

jasonjckn09:06:36

so you still need a reduce step, yah that's true, unless you materialize the reducible on eval

jasonjckn09:06:44

i think if clojure had reducers from v1.0 / the beginning, they would have done an eduction on eval or something.

jasonjckn09:06:02

just way more usable in the REPL. the way lazy seq is. too late now though.

p-himik09:06:12

Assuming I understand your problem correctly, it seems that if you use the transducer syntax and the last reducing step is not something that lets you view the whole sequence, you can just temporarily change that last step. Which seems roughly the same in terms of the amount of work you need to do with reducers. In other words, if you have something like

(transduce (comp ...)
           + 0 coll)
and you want to view all the steps, you can just change it to
(transduce (comp ...)
           conj [] #_#_+ 0 coll)

jasonjckn09:06:09

Yah that's fair, i guess I exaggerated it in my mind the differences in convenience

exitsandman09:06:47

anyway, most of the core seq transformations can in fact be re-implemented as transducers, but some would require to basically keep the entire reduction state in your transducer (e.g. sort and take-last), which is why they weren't implemented, I suspect.

jasonjckn10:06:26

that's what clojure.core/sort does anyways

jasonjckn10:06:58

its neither implemented lazy, nor, step-wise.

exitsandman10:06:28

(slack what?)

p-himik10:06:33

Just call dedupe. :)

exitsandman10:06:40

For instance, here's how a sort transducer could look like (now with 100% less garbled code)

(defn sorting
  ([] (sorting compare))
  ([cmp]
   (fn [rf]
     (let [xs (java.util.ArrayList.)]
       (fn
         ([] (.clear xs) (rf))
         ([res]
          (rf (reduce rf res (doto xs (.sort cmp)))))
         ([res in]
          (.add xs in) res))))))

jasonjckn10:06:54

here's the print-method with the reducible, at least i got to the end of my exercise, albeit i probably won't do this in my codebase

exitsandman10:06:14

anyway, isn't eduction basically what you're looking for?

jasonjckn10:06:16

me? eduction is great , i would have liked to use that in print-method, but i can't get a coll from CollReduce protocol.

jasonjckn10:06:23

anyways I think I answered all my questions. (with all your help)

p-himik10:06:12

I just have to say, your choices and combination of the font and the coloring scheme are the most unique I've ever seen a programmer use. That lower-case N is unholy. :D

💯 2
jasonjckn10:06:29

@U2FRKM4TW haha thanks i guess, its all custom, i probably have the only emacs theme with two foreground colors, both black and white. Font is also my own creation.

jasonjckn10:06:55

https://github.com/jasonjckn/tensora-mono-font i just paid a font designer to tweak it, a couple hours ago (in progress), since im not actually a graphic designer, but its my favorite programmer font

wikipunk13:06:18

I don’t understand why you are doing any of this