This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-11-28
Channels
- # adventofcode (2)
- # aleph (4)
- # announcements (3)
- # asami (13)
- # babashka (27)
- # beginners (74)
- # clj-kondo (18)
- # cljdoc (1)
- # cljs-dev (27)
- # cljsrn (5)
- # clojure (75)
- # clojure-australia (5)
- # clojure-europe (25)
- # clojure-uk (2)
- # conjure (1)
- # core-logic (1)
- # deps-new (1)
- # fulcro (13)
- # gratitude (8)
- # honeysql (1)
- # lsp (24)
- # missionary (45)
- # mount (2)
- # nextjournal (24)
- # off-topic (10)
- # pathom (5)
- # portal (3)
- # releases (1)
- # shadow-cljs (7)
- # specter (1)
- # tools-deps (1)
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
https://clojuredocs.org/clojure.core/deftype#example-6012fbc4e4b0b1e3652d743f ^:unsynchronized-mutable instead of volatile-mutable.
Another hack is using a slot of a primitive array in a deftype that implements a definterface with primitive typed methods
Particularly useful if you need multiple prims of the same type
But of course, you need to be much more careful about the concurrency and visibility
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
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.
https://github.com/clojure/clojure/commit/2a09172e0c3285ccdf79d1dc4d399d190678b670
> ;;flushing ops must clear before invoking possibly > ;;failing nested op, else infinite loop
Interesting. Would love to understand how that could happen?
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
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!
Thanks for the warning. I really would like to understand why though - it smacks of "voodoo programming" otherwise 😱
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.
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...-
You're correct, you have to specify an empty array to signify no arguments to a vararg method.
but i cant seem to make Foo resolve without running the import in a seperate statement
works
user=> (do (import (java.util HashMap)) (let [x HashMap] (type x)))
java.lang.Class
user=>
maybe be more specific about what isn't workingMaybe 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
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
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)}})
Syntax error compiling at (shared-src/lumanu/com/company/todo/transit.cljc:5:7).
Unable to resolve symbol: Todo in this context
so in clojure its an easy fix -
(eval `(do (require (quote [~model-namespace]))
(import ~(symbol
(str
(csk/->snake_case_string model-namespace)
"."
model-name))))))
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.
nah, the issue is top level do forms are broken apart and each seperately compiled and executed
which means in a toplevel do form, the import macro can run before the let is compiled
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
and why wouldn't using Class/forName
or importing the class when the macro is called and returning the code fix it?
because the code that loads the namespace hasn't run yet, so the type is not created yet
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
(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})
(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))
now the reason the macro doesn’t just generate the vars is that cursive can’t handle that
yeah, the only way to make that work is if your macro loads code as a sideeffect, which is gross
being in a let like means it is all part of the same compilation unit (the same top level form)
loading code as a side effect is how clojure works for defrecord
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
> 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
so clojurescript should just see a require and then a usage, but the require is in the let
the compilation model for clojurescript is very different, because of the way the meta lanaguage for clojurescript is clojure, not clojurescript
(let [{:keys [read-handlers]} (do (require '[com.company.thing.model])
{"tag" com.company.thing.model/map->Thing})])
it might, it might lean more on runtime effects instead of mutating the compilation environment