Fork me on GitHub
#clojure
<
2021-11-28
>
hugod03:11:35

Is it possible to get a mutable primitive place? I thought that this might work, but doesn’t seem to

(defprotocol P
  (add! [_ ^long x]))

(deftype T
    [^:volatile-mutable ^long c]
  P
  (add! [_ x]
    #_(set! c  x)))
(add! (->T 0) 1)

==> class user$eval42522$fn__42523$G__42513__42530 cannot be cast to class clojure.lang.IFn$OLO

hugod03:11:27

It looks like using an interface rather than a protocol works

Alex Miller (Clojure team)12:11:00

Another hack is using a slot of a primitive array in a deftype that implements a definterface with primitive typed methods

Alex Miller (Clojure team)12:11:54

Particularly useful if you need multiple prims of the same type

Alex Miller (Clojure team)12:11:39

But of course, you need to be much more careful about the concurrency and visibility

hugod12:11:22

Is it a bug that it can’t be done with deftype and a protocol?

Benjamin08:11:07

Is there a codeq analyzer for csharp?

paulbutcher15:11:04

As part of my journey of getting my head properly around transducers, I’m looking at the source of partition-all. I’m confused by one part of it: why is the call to .clear necessary in the completion arity? What would go wrong if this line was deleted? As far as I can see, there’s no need to clear it because the completion arity will only ever be the last call to a transducer? I suspect I’m missing something though? https://github.com/clojure/clojure/blob/17ae75eadbb93152e460cd156adeb7876e616631/src/clj/clojure/core.clj#L7258

rutledgepaulv16:11:54

from looking at the original commit it sounds like since the .isEmpty check is being used to branch there was a possibility if you don't clear first that there could be a re-entrant infinite loop.

rutledgepaulv16:11:24

> ;;flushing ops must clear before invoking possibly > ;;failing nested op, else infinite loop

paulbutcher16:11:21

Interesting. Would love to understand how that could happen?

rutledgepaulv16:11:33

I'm not sure what the scenario is. i guess if you tried to reduce a collection containing your stateful reduce function using your reduce function you could probably worm your way into that situation

phill16:11:47

I don't know either, but I ran into it in conjunction with core-async channels (and my own transducer, wherein I said to myself, ha ha no need to clear). Heed well that the "clear first!" comments in the partition fns are the ONLY exclamation-point comments in all of clojure core!

paulbutcher16:11:49

Thanks for the warning. I really would like to understand why though - it smacks of "voodoo programming" otherwise 😱

Jan K18:11:14

I think in theory you could create a "transducible process" where the transducer-wrapped reducing function is used for multiple reductions for some reason, and then the missing clear would be a problem. If you're using transduce there is no problem as the wrapped reducing function is not reused.

hanDerPeder21:11:12

Just want to make sure I understand interop with vararg methods. In Java given a method signature void foo(int… someNumbers) you can call it like obj.foo(). In Clojure, you can’t leave out the argument, right? You have to explicitly provide the argument to call the correct arity method. Ie, (.foo obj (int-array [])). Using the google cloud sdk and they sure like varags. https://javadoc.io/doc/com.google.cloud/google-cloud-storage/latest/com/google/cloud/storage/Blob.html#signUrl-long-java.util.concurrent.TimeUnit-com.google.cloud.storage.Storage.SignUrlOption...-

p-himik21:11:49

You're correct, you have to specify an empty array to signify no arguments to a vararg method.

👍 1
emccue21:11:27

trying to write a macro that will do this

emccue21:11:00

(do (import (thing Foo))
    (let [x Foo]))

emccue21:11:31

but i cant seem to make Foo resolve without running the import in a seperate statement

hiredman21:11:27

works

user=> (do (import (java.util HashMap)) (let [x HashMap] (type x)))
java.lang.Class
user=>
maybe be more specific about what isn't working

p-himik21:11:16

Maybe the quoting is wrong. This also works:

user=> (defmacro m [] `(do (import ~'(x X)) (let [x# ~'X] (println x#))))
#'user/m
user=> (m)
x.X

hiredman21:11:18

my guess would be you are trying to do something like (let [x String] (new x "foo")) which as you have found out, you cannot do

hiredman21:11:44

(you cannot do using clojure's interop, you have to use java reflection directly)

emccue21:11:55

nah just trying to generate transit handlers

emccue21:11:11

and it seems to only break if the class hasn’t been loaded in yet

emccue21:11:16

so List and HashMap work

emccue21:11:34

trying to find an obscure standard library class to test

hiredman21:11:14

seems unlikely

emccue21:11:49

this is the generated code that wasn’t working from a fresh repl

(do
 (clojure.core/require (quote [com.company.todo.model]))
 (clojure.core/import (com.company.todo.model Todo))
 {:write-handlers (clojure.core/let
                   [model__45767__auto__ Todo]
                   {model__45767__auto__ (com.company.util.transit/record-writer "todo")}),
  :read-handlers {"todo" (com.company.transit/record-reader
                                        com.company.todo.model/map->Todo)}})

hiredman21:11:57

and what error do you get doing what?

emccue21:11:09

Syntax error compiling at (shared-src/lumanu/com/company/todo/transit.cljc:5:7).
Unable to resolve symbol: Todo in this context

emccue21:11:27

but if i run each one of those statements seperately it works

hiredman21:11:34

the call to import may need quoting

hiredman21:11:11

ah, but that do is embedded in a larger expression

hiredman21:11:18

which is why it returns a map

hiredman21:11:30

which cause the whole do to be compiled and evaluated at once

hiredman21:11:36

instead of form by form

hiredman21:11:57

which means what the let is being compiled, the import hasn't run yet

emccue21:11:26

so in clojure its an easy fix -

(eval `(do (require (quote [~model-namespace]))
                 (import ~(symbol
                           (str
                             (csk/->snake_case_string model-namespace)
                             "."
                             model-name))))))

emccue21:11:44

but if i am expanding the macro in cljs the compiler doesnt complain

emccue21:11:52

which i am now suspicious of

hiredman21:11:54

the fix is to re-write things so the do is a top level form

phronmophobic21:11:02

It seems like requiring com.company.todo.model actually creating a class. For that, you can either use Class/forName or do what some of the clojure core functions do: create the class when the macro is called and return the code that creates the class.

hiredman21:11:34

nah, the issue is top level do forms are broken apart and each seperately compiled and executed

🤯 1
hiredman21:11:53

which means in a toplevel do form, the import macro can run before the let is compiled

hiredman21:11:35

but when the do is not a top level form, the whole thing is compiled as a single expression, which means the import hasn't run, and its side effect hasn't changed the compilation environment for the let

hiredman21:11:45

"the gilardi scenario"

phronmophobic21:11:43

and why wouldn't using Class/forName or importing the class when the macro is called and returning the code fix it?

emccue21:11:59

here is exactly the boilerplate i am trying to replace

hiredman21:11:12

because the code that loads the namespace hasn't run yet, so the type is not created yet

hiredman21:11:11

I dunno what the structure of whatever you are doing is, but basically you need to lift compilation environment effecting forms (require, import, etc) out of subexpressions and hoist them into an enclosing do

emccue21:11:16

(ns com.company.private-note.transit
  (:require
   [com.company.private-note.model :as model]
   [com.company.transit :as transit])
  #?(:clj
     (:import [com.company.private_note.model PrivateNote])))

(def transit-tag "private-note")

(def private-note-writer (transit/record-writer transit-tag))
(def private-note-reader (transit/record-reader model/map->PrivateNote))

(def write-handlers
  (let [private-note #?(:clj PrivateNote :cljs model/PrivateNote)]
    {private-note private-note-writer}))

(def read-handlers {transit-tag private-note-reader})

emccue21:11:08

(ns com.company.todo.transit
  (:require [com.company.transit :as transit]))

(let [{:keys [read-handlers write-handlers]}
      (transit/generate-transit-handlers
       "com.company.todo.model/Todo")]
  (def read-handlers read-handlers)
  (def write-handlers write-handlers))

emccue21:11:16

and this^ is what i came up with

emccue21:11:49

now the reason the macro doesn’t just generate the vars is that cursive can’t handle that

hiredman21:11:02

yeah, the only way to make that work is if your macro loads code as a sideeffect, which is gross

hiredman21:11:47

being in a let like means it is all part of the same compilation unit (the same top level form)

phronmophobic21:11:49

loading code as a side effect is how clojure works for defrecord

hiredman21:11:07

loading code as a side effect of macro expansion

emccue21:11:07

and the reason i don’t make the macro just put handlers into a mutable registry is that there is a lot of code to change for that right now

emccue21:11:43

> loading code as a side effect of macro expansion So yeah, im fine with this i guess, i just don’t understand why the clojurescript compiler seems okay with it

emccue21:11:02

i guard the loading with a (when (:ns &env))

hiredman21:11:10

imports work differently in clojurescript, but I know less about it

emccue21:11:26

so clojurescript should just see a require and then a usage, but the require is in the let

hiredman21:11:28

the compilation model for clojurescript is very different, because of the way the meta lanaguage for clojurescript is clojure, not clojurescript

emccue21:11:29

(let [{:keys [read-handlers]} (do (require '[com.company.thing.model])
                                  {"tag" com.company.thing.model/map->Thing})])

emccue21:11:01

does it maybe “lift” requires, even if they are nested in expressions?

hiredman21:11:41

it might, it might lean more on runtime effects instead of mutating the compilation environment

hiredman21:11:35

it would make a lot of sense if the compiler lifted requires and imports, I just don't know

hiredman21:11:04

maybe I have it reversed, clojurescript relies less on runtime effects, and instead uses macros that have side effects when expanded