This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-11-30
Channels
- # admin-announcements (1)
- # adventofcode (2)
- # announcements (2)
- # babashka (60)
- # beginners (48)
- # cherry (1)
- # cider (16)
- # clj-kondo (4)
- # clojure (53)
- # clojure-belgium (3)
- # clojure-europe (20)
- # clojure-nl (1)
- # clojure-norway (6)
- # clojure-poland (4)
- # clojure-uk (6)
- # clojuredesign-podcast (19)
- # clojurescript (39)
- # community-development (12)
- # cursive (4)
- # datalevin (7)
- # datomic (23)
- # honeysql (14)
- # hyperfiddle (3)
- # instaparse (3)
- # lsp (3)
- # malli (10)
- # off-topic (34)
- # overtone (8)
- # polylith (2)
- # re-frame (9)
- # reitit (3)
- # releases (1)
- # squint (16)
- # timbre (7)
- # wasm (2)
- # xtdb (8)
Is there a way to make clojure.core/conj
operate on java.nio.file.Path
?
so instead of this:
(-> " " java.net.URI. java.nio.file.Path/of
(as-> p
(reduce #(.resolve ^java.nio.file.Path %1 ^String %2)
p
["asd" "qwe/zxc"])))
; => #object[software.amazon.nio.spi.s3.S3Path 0x66f6cc75 "/asd/qwe/zxc"]
i can just write this:
(-> " " java.net.URI. java.nio.file.Path/of (conj "asd" "qwe/zxc"))
i tried
(extend-type java.nio.file.Path
clojure.lang.IPersistentCollection
(cons [^java.nio.file.Path p ^String more]
(.resolve p more)))
but got this error:
Execution error (IllegalArgumentException) at ...
interface clojure.lang.IPersistentCollection is not a protocol
which is true, but disappointing.
iirc, Rich Hickey mentioned in one of his talks that if he could start over again, he would have used protocols as the basis of clojure.core
instead of interfaces. (or something along those lines)
i guess that would have allowed this kind of extension, right?I think you’re right. Maybe you could use proxy or reify to create a custom imolementation of Path, but I’m not really sure. But - if we rewind a bit - why do you want to overload conj with this particular operation? This example seems like a perfect use case for a clearly named utility function. I would be surprised to see someone bolt this quite domain specific string-like thing to conj or other collection operations in real life code.
i just felt conj
would make (some) sense, because it already has the iteration built into it and i would rather use existing core functions then introduce new ones, which feel like parochialism.
i did write these utility functions at the end:
(defn- join1 [^Path p segment]
(.resolve p segment))
(defn join [^Path p & segments]
(reduce join1 p segments))
called it join
because the standard libs in other languages call it join. (resolve doesn't make a lot of sense to me)join
sounds like it would turn a
and /b
into a//b
or a/b
.
resolve
makes it sound more like it turns it into /b
.
Though in Python both os.path.join
and pathlib.Path.joinpath
work as the latter, but it's not immediately intuitive from the name, and the second function doesn't document the behavior.
it's only a protocol in clojurescript like you said https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L600
Heh, C++ didn't even add a method for it - it's just overloading the /
operator (Python also does that, but there's also a method).
C# calls it Combine
, I think it's also more clear and intuitive than join
/`conj`/"concat" (what C++ calls it in the docs)/`/`.
BTW if you don't need that behavior with absolute segments, any name would do. And you might already be able to use io/file
for that (no clue about other protocols - don't have them):
(-> "file:///a/b" java.net.URI. java.nio.file.Path/of
(io/file "c" "d" "e")
.toPath)
=> #object[sun.nio.fs.UnixPath 0x51818c79 "/a/b/c/d/e"]
yeah, the problem with mixing io/file
into the picture is that it's not aware of the protocol/`java.nio.file.FileSystem` concept, which is why we are transitioning to using Path
s
making conj
work would imply that into
would work too, since into
uses conj
as its reduction function by default, so (into p ["asd" "qwe/zxc"])
should work too.
combine
doesn't suggest ordering, but something more intricate.
Hi everyone, apologies if this has already been asked tons of times. Do you know why the primitive ^long
type hint causes that reflection warning? Looks like the return type is ignored (looking into an Object
instead of a String
) and that it's looking for the field length
(instead of the method)
(set! *warn-on-reflection* true)
(defn append-1 ^String [^String s ^long n] (str s "-" n))
(defn append-2 ^String [^String s ^Long n] (str s "-" n))
(.length (append-1 "some-string" 1)) ; => Reflection warning - reference to field length on java.lang.Object can't be resolved.
(.length (append-2 "some-string" 2)) ; => No warnings
When you use primitive type hints for a function, the compiler tries to find the matching interface in IFn
. In this case, it finds this:
static public interface OLO{Object invokePrim(Object arg0, long arg1);}
Interfaces with primitives take higher priority, so the function ends up being essentially (defn append-1 [s ^long n] ...)
.And the fix here would be to remove type hints from the arguments. Or at least from the second argument.
Thanks for the answer! I think I'll remove the type hint in the argument and move it in the body of the function
Why do you need type hints there at all? Except for the return value. This is where the matching interface is determined: https://github.com/clojure/clojure/blob/4090f405466ea90bbaf3addbe41f0a6acb164dbb/src/jvm/clojure/lang/Compiler.java#L3862 This is the matching interface itself: https://github.com/clojure/clojure/blob/43873e477dff33e5c53323bf8ceddc014b870a40/src/jvm/clojure/lang/IFn.java#L109
Thank you! I stripped down the example so it's not clear, but I need the type hint in the argument to be able to call a java method that expects a primitive long
and I don't want to wrap it into a Long
which is not necessary
I think this is a bug. Matching against a primitive interface shouldn't prevent type hints from propagating. Does this deserve an AskClojure thread, @U064X3EF3?
I can create the thread on AskClojure and post the link here if you want (and didn't already)
Done: https://ask.clojure.org/index.php/13501/primitive-type-hints-arguments-turns-the-return-value-object, sorry for the delay
Hi everyone. Question specific to dtype-next. Fantastic library, wish there was more tutorials / documentation. I have a simple struct :
(dt-struct/define-datatype! :vec3_t [{:name :x :datatype :float32}
{:name :y :datatype :float32}
{:name :z :datatype :float32}])
I have C API expecting an array of these struct:
void some_func(vec3_t* data);
I'm building an array of struct (I'm parsing a list of vertices, parsed-content) :
(def vertices-array (dt-struct/new-array-of-structs :vec3_t (count (:vertices parsed-content))))
Then I want to set all the struct from my array :
(let [xcol (dt-struct/array-of-structs->column vertices-array :x)
ycol (dt-struct/array-of-structs->column vertices-array :y)
zcol (dt-struct/array-of-structs->column vertices-array :z)]
(doseq [idx (range (count vertices-array))
[x y z] (:vertices parsed-content)]
(dt/set-value! xcol idx (Float/parseFloat x))
(dt/set-value! ycol idx (Float/parseFloat y))
(dt/set-value! zcol idx (Float/parseFloat z)))
)
Not sure this is the best way to do it. It seems to work, but I'm ending up with all the
elements set to the same values !
[{:x -12.250777, :y -3.481309, :z 7.733591} {:x -12.250777, :y -3.481309, :z 7.733591} ...]
2 questions :
* What is the canonical way of building array of structs ?
* Any idea why i'm ending up with the same values for all elements ?> * Any idea why i'm ending up with the same values for all elements ?
most likely because array-of-structs->column
returns a new object. So any manipulations with it will not be reflected in vertices-array
Ok it's not because there is a copy but because I didn't use doseq correctly, it wil iterate i * j times and not i times binding values for idx and [x y z] (if that makes sense)
If I have a schema definition with clojure spec defined with s/keys
, and I would like to transform that schema and make all of the keys optional, what is the easiest way to do it?
I mean programatically
like I have something like this:
(s/def ::test (s/keys :opt-un [::foo] :req-un [::bar]))
and I need a way to programmatically convert that to a definition of:
(s/keys :opt-un [::foo ::bar])
Perhaps if you know under which condition you need the different formats, you could test for that condition and use the correct spec? Or is that not what you are looking for?
I have a definition where certain keys are required, and in other use-cases all of the keys are optional. I don’t want to write the spec down again, to avoid mistakes.
You can write a macro that does that - you'd have two separate specs and you'd choose between them at run time.
Maybe you can use spec-tools
for that, not sure.
But if you need programmatic spec transformation more than once, I'd choose a different tool, e.g. malli
.
I’ve came up with this so far:
(defmacro all-keys-optional [spec]
(let [{:keys [req req-un opt opt-un]} (apply hash-map (drop 1 (s/form spec)))
optional {:opt (vec (concat req opt))
:opt-un (vec (concat req-un opt-un))}
spec-def (into {} (filter (comp not-empty second) optional))]
spec-def
#_(s/keys ~@spec-def)))
if it's clj (not cljs) you can juse use eval and skip the macro, depends how much you have to do this
I guess you could also make a macro that would parse the s/form of the original spec and write the new one for opts
I have hard time applying the map as keyword args to s/keys
that is true
(defn all-keys-optional [spec]
(let [{:keys [req req-un opt opt-un]} (apply hash-map (drop 1 (s/form spec)))
optional {:opt (vec (concat req opt))
:opt-un (vec (concat req-un opt-un))}
spec-def (apply concat (filter (comp not-empty second) optional))]
(eval `(s/keys ~@spec-def))))
so this should be working, right?
thank you!
good point!
Hehe there was just a clojure shout out from Nubank at aws reinvent keynote :)