This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-12
Channels
- # ai (1)
- # announcements (7)
- # babashka (32)
- # beginners (23)
- # biff (9)
- # calva (1)
- # cljs-dev (13)
- # clojure (32)
- # clojure-belgium (1)
- # clojure-chicago (15)
- # clojure-europe (24)
- # clojure-india (3)
- # clojure-nl (3)
- # clojure-norway (55)
- # clojure-uk (4)
- # clojurebridge (1)
- # clojurescript (5)
- # core-async (17)
- # data-science (9)
- # datomic (29)
- # events (3)
- # fulcro (16)
- # graalvm-mobile (4)
- # helix (15)
- # hyperfiddle (74)
- # introduce-yourself (1)
- # jobs (4)
- # kaocha (12)
- # leiningen (27)
- # lsp (16)
- # shadow-cljs (6)
- # spacemacs (20)
- # sql (27)
- # squint (7)
- # tools-deps (29)
- # vim (2)
- # xtdb (10)
A "case + int + reflection warning" question. This code triggers a warning although the type hint is set:
(let [^int result (get-some-result)]
(case result
0 :foo
1 :bar))
;; case has int tests, but tested expression is not primitive.
But this code is OK:
(let [result (get-some-result)]
(case (int result)
0 :foo
1 :bar))
I've been using (int case-expression)
for years but now I'd like to know does it work so?A more general question is, that where should I put a type hint to avoid coercing to (int ...)
explicitly?
0 and 1 are longs here, not ints
but not sure if your warning was from clojure or linter?
Performance warning, /home/foo/Bar/xxx/api/src/model/foo.clj:77:11 - case has int tests, but tested expression is not primitive.
> (defn get-some-results ^long [] 1)
Fairly sure that a defn cannot return a primitive type - primitives don't survive across clojure function call boundaries
Likewise you cannot simply:
(let [^long result (get-some-result)])
...you should instead write:
(let [result (long (get-some-result))])
Doing otherwise may silence warnings, which doesn't necessairly mean they're effectively addressed :)
I recommend giving https://clojure.org/reference/java_interop#primitives a careful lookWell, this works fine:
(defn get-foo ^long [] 42)
(let [result (get-foo)]
(case result
1 :boo
2 :baa
42 :lol))
:lol
Functions have limited support for primitive arguments and return type: type hints for long
and double
(only these) generate primitive-typed overloads. Note that this capability is restricted to functions of arity no greater than 4.
> Fairly sure that a defn cannot return a primitive type it does for ^long or ^double
I believe, here is a list of all the possible types combination: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java#L97
yes, all combinations of ^long or ^double up to arity 4
It seems that folders in the classpath aren't 100% dynamic. Is this correct?
When a folder doesn't exist at startup, it doesn't seem to make it into the classpath even if specified in deps.edn. This tripped me up because the target
folder is often in .gitignore
and doesn't exist until the first process startup ๐งต
Here's the surprising (to me!) repl session. This is running in an empty folder:
~/temp$ % clj -Sdeps '{:paths ["src" "target"]}'
Clojure 1.11.1
user=> (slurp ( "file.txt"))
Execution error (IllegalArgumentException) at user/eval1 (REPL:1).
Cannot open <nil> as a Reader.
user=> (clojure.java.shell/sh "mkdir" "-p" "target")
{:exit 0, :out "", :err ""}
user=> (spit "target/file.txt" "hi")
nil
user=> (slurp ( "file.txt"))
Execution error (IllegalArgumentException) at user/eval7 (REPL:1).
Cannot open <nil> as a Reader.
user=>
~/temp$ % clj -Sdeps '{:paths ["src" "target"]}'
Clojure 1.11.1
user=> (slurp ( "file.txt"))
"hi"
As you can see, resource
doesn't find file.txt
until I restart the clojure
process. I'm wondering if this is a java or tools.deps
behavior, and if people have found a good workaround for "non-existent (at startup) folders containing resources"
not really an answer to the question, but I sometimes use a target/.gitkeep
file to avoid errors like this. Then I check it in with git add -f target/.gitkeep && git commit
.
I believe it's a JVM thing. Adding class files to an existing classpath is relatively dynamic on the JVM. Adding new paths to a running JVM is often requires a custom class loader (similar to web servers like tomcat). The easiest approach is to create the necessary directories before running the repl (or restart the repl after adding a new path). Not sure if the new hot loading tools on Clojure 1.12 support paths. I think hot loading jars is more amenable that paths in the JVM (but it's been a decade since I last did that sort of thing)
Is there a way for me to expose an external library function with its docstring/multiple clauses/etc in my own namespace? For example, I'd like to transparently wrap something like:
(ns external.funny-math)
(defn funny-addition
"This adds some numbers, most of the time"
([x] x)
([x y] (+ x y))
([x y z] "no addition here, try more arguments")
([x y z & ns] (apply + x y z ns)))
I could do
(ns internal.maths
(:require [external.funny-math :as fm]))
(def funny-addition fm/funny-addition)
which works from a functionality standpoint when I call internal.maths/funny-addition
, but this doesn't let me naturally view the docstring/arities that I normally would be able to if I inspected the external library function itselfYou can do something like this:
(def funny-addition fm/funny-addition)
(alter-var-meta #'funny-addition #(merge % (select-keys (meta #'fm/funny-addition) [:doc :arglists])))
The library potemkin does this for you, although I believe it's usually considered unidiomatic these days, mostly because it means that any kind of "go to definition" functionality will give you confusing results.
The honeysql 1 library has a util you could use or reference: https://github.com/seancorfield/honeysql/blob/v1.0.461/src/honeysql/util.clj#L3
ahhhh very interesting, appreciate the inputs! maybe it'd just be preferred to copy over the :arglists and :doc, I think thats all I'd want to "forward" over
and that shouldn't mess up the go-to-defs
In case it helps, cider-nrepl latest detects the (def funny-addition fm/funny-addition)
pattern and reflects all relevant metadata, in a non-intrusive way (nothing is mutated).
If you use other tooling, it's a sensible feature to request.
that does not add the var to ns-publics of the current ns which means it's unavailable to consumers of the referring namespace.