This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-07-29
Channels
- # announcements (10)
- # aws (4)
- # babashka (42)
- # beginners (208)
- # bristol-clojurians (2)
- # calva (13)
- # chlorine-clover (52)
- # cider (5)
- # clara (22)
- # clj-kondo (2)
- # clojars (14)
- # clojure (107)
- # clojure-europe (24)
- # clojure-nl (4)
- # clojure-uk (6)
- # clojurescript (3)
- # conjure (20)
- # cursive (23)
- # datascript (2)
- # datomic (15)
- # figwheel-main (3)
- # fulcro (23)
- # jobs (3)
- # kaocha (5)
- # keechma (3)
- # local-first-clojure (1)
- # malli (13)
- # off-topic (16)
- # planck (6)
- # re-frame (8)
- # reagent (3)
- # reitit (1)
- # releases (1)
- # remote-jobs (1)
- # rum (1)
- # sci (37)
- # shadow-cljs (16)
- # tools-deps (158)
- # vim (3)
- # xtdb (8)
I run my code in the REPL, it works OK and returns what I expect. I run it in a unit test and I see different results š
The code is very simple
(defn gcdi [a b]
(loop [a (Math/abs a)
b (Math/abs b)]
(if (zero? b)
a
(recur b (mod a b)))))
(defn lcmu [a b]
(/ (* (Math/abs a) (Math/abs b)) (gcdi a b)))
(defn oper-array [fct arr init]
(reductions fct arr))
I expect
(oper-array lcmu [53 83 54 -58 -20 56 57 10] 53)
=> (53 4399 68888340 964436760 18324298440 18324298440)
And
(lcmu 18324298440 10)
=> 18324298440
SO I write the test:
(deftest a-test
(testing "oper" (is (= [53 4399 68888340 964436760 18324298440 18324298440]
(oper-array lcmu [53 83 54 -58 -20 56 57 10] 53))))
(testing "lcmu"
(is (= 18324298440 (lcmu 18324298440 10)))))
both fail, but you can see in the REPL i get th results I expect@kaxaw75836 =
will correctly compare a vector to a list.
That's not about tests. That's just how =
works in Clojure.
https://clojure.org/guides/equality might be good reading.
@qmstuart I suspect your REPL state is out of sync with your code since running tests should be starting fresh each time, based on what's in the source files, where your REPL is whatever you've evaluated into it.
@seancorfield, I killed the REPL and restarted it. Same results.
I get (lcmu 18324298440 10) => 5722146280
My results
user=> (lcmu 18324298440 10)
18324298440
user=> (oper-array lcmu [53 83 54 -58 -20 56 57 10] 53)
(53 4399 68888340 964436760 18324298440 18324298440)
JVM versions? Clojure versions?
Clojure 1.10.1, Java 11.0.8
I'm on OpenJDK 14 and 1.10.2-alpha1 respectively.
user=> (clojure-version)
"1.10.2-alpha1"
user=> (System/getProperty "java.version")
"14"
@mitchell_clojure that is the result I get when run via the test
OK, I'd suggest running the tests via the command-line, outside Cursive. It's a Leiningen project? So lein test
in the project directory should do it.
Just looking at the functions, the lcm of 18324298440 and 10 is not 18324298440, because 10 divides it
FAIL in (a-test) (core_test.clj:6)
oper
expected: [53
4399
237546
6888834
68888340
964436760
18324298440
18324298440]
actual: (53
4399
237546
6888834
68888340
964436760
18324298440
5722146280)
diff: - [nil nil nil nil nil nil nil 18324298440]
+ [nil nil nil nil nil nil nil 5722146280]
lein test :only oper.core-test/a-test
FAIL in (a-test) (core_test.clj:9)
lcmu
expected: 18324298440
actual: 5722146280
diff: - 18324298440
+ 5722146280
So... we both get the same (wrong) answer in the REPL...?
oh right, because one is the multiple of the other
When I step through lcmu
with the debugger, it evaluates (Math/abs a)
to 1144429256
...
Yeah, in my repl I get the same, but when stepping through the debugger that is the point where things go wrong
It's the difference between Java 11 and Java 14.
So I think some how your tests are running on a different JDK version to your REPL...
Clojure 1.10.1
user=> (System/getProperty "java.version")
"11.0.5"
...
user=> (lcmu 18324298440 10)
gcdi 18324298440 10
loop 1144429256 10
loop 10 6
loop 6 4
loop 4 2
loop 2 0
5722146280
(I added a couple of prints in there)
(defproject oper "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url ""
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url " "}
:dependencies [[org.clojure/clojure "1.10.1"]]
:main ^:skip-aot oper.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}
:dev {:plugins [[com.jakemccrary/lein-test-refresh "0.24.1"]
[io.aviso/pretty "0.1.37"]]
:dependencies [[pjstadig/humane-test-output "0.10.0"]
[io.aviso/pretty "0.1.37"]]
:injections [(require 'pjstadig.humane-test-output)
(pjstadig.humane-test-output/activate!)]}})
?When I run the same code above on JDK 8 I get the 5722146280 value
I ran the exact same REPL session on JDK 14 and I get this
user=> (lcmu 18324298440 10)
gcdi 18324298440 10
loop 18324298440 10
loop 10 0
18324298440
So Math/abs
seems to behave differently.
using type annotations seems to fix it for me
(defn gcdi [a b]
(loop [a (Math/abs ^long a)
b (Math/abs ^long b)]
(if (zero? b)
a
(recur b (mod a b)))))
(defn lcmu [a b]
(/ (* (Math/abs ^long a) (Math/abs ^long b)) (gcdi a b)))
so I think that there is some type trickery happening here
Ah, so it could be a difference in overloading between JDK versions perhaps?
According to the Java docs, there was no change there. That is definitely a weird one.
BUt thank you guys so much! It's 2:42 am here and this has been bugging all night š
@kaxaw75836 No, it can't be that -- we're using the same version of Clojure
(i.e., Clojure isn't changing between the failing and successful runs)
The difference is overload resolution based on changes in the JDK it seems...
What is the lesson here? Use numeric-tower and stay away from java methods?
for me the lesson is that you can have an integer overflow and no exceptions without requesting unchecked math
@seancorfield looks like a bug in clojure between different java versions? java 14 should failt too, let/loop-bound locals can be of primitive types, having the inferred, possibly primitive type of their init-form.
@kaxaw75836 You're misinterpreting that.
Ok, was thinking with java 8 the let is interpreting the primitive type correctly, an int, with java 14 is interpreted as a long
user=> (defn f [x] (Math/abs x))
#'user/f
user=> (f 18324298440)
1144429256
^ I think this is really impressiveIt looks like a change in reflection, based on a difference in the JDK.
(! 889)-> JAVA_HOME=$OPENJDK14_HOME clj
Clojure 1.10.1
user=> (System/getProperty "java.version")
"14"
user=> (set! *warn-on-reflection* true)
true
user=> (defn foo [x] (Math/abs x))
Reflection warning, NO_SOURCE_PATH:1:15 - call to static method abs on java.lang.Math can't be resolved (argument types: unknown).
#'user/foo
user=> (foo 18324298440)
18324298440
user=> ^D
Tue Jul 28 18:55:22
(sean)-(jobs:0)-(~/clojure)
(! 890)-> JAVA_HOME=$OPENJDK11_HOME clj
Clojure 1.10.1
user=> (System/getProperty "java.version")
"11.0.5"
user=> (set! *warn-on-reflection* true)
true
user=> (defn foo [x] (Math/abs x))
Reflection warning, NO_SOURCE_PATH:1:15 - call to static method abs on java.lang.Math can't be resolved (argument types: unknown).
#'user/foo
user=> (foo 18324298440)
1144429256
user=>
In both cases, Clojure has to fall back on reflection to figure out the call (so it's relying on the underlying JDK for that) and in JDK 8 and 11 reflection tells it one thing, but in JDK 14 it tells it something different.Math/abs
is overloaded on int
, long
, float
, and double
-- the return type matches the argument type.
Well, a "meta-lesson" is to put (set! *warn-on-reflection* true)
in every file š Then you can actually see where Clojure needs some help.
I tried to find some indication that reflection changed across those JDK versions but it's kinda hard to search for.
@qmstuart Definitely an interesting edge case to get bitten by...
but the scary thing is that it's not a performance thing, it's a correctness thing now, sorta
Yup, I think I've seen a handful of similar issues over the years, but that's probably the simplest repro case I've ever seen š
@seancorfield so it's not the job of let to infer the primitive type?
The Clojure compiler will infer a primitive type in some situations.
Here's an example that cropped up the other day that surprised someone:
user=> (loop [x (pos? 1)] (when-not x (recur true)))
Syntax error (IllegalArgumentException) compiling fn* at (REPL:1:1).
recur arg for primitive local: x is not matching primitive, had: java.lang.Boolean, needed: boolean
user=> (loop [x (true? 1)] (when-not x (recur true)))
nil
user=>
pos?
is an inline function that calls clojure.lang.Numbers/isPos
which is declared to return boolean
(primitive).
true?
is a Clojure function type-hinted to return Boolean
(not a primitive).
and yet this version does not trigger the syntax error -- instead it runs (but blows up trying to call pos?
on nil
after the recur
)
user=> (loop [x (pos? 1)] (when-not x (recur nil)))
Syntax error (NullPointerException) compiling fn* at (REPL:1:1).
null
And then you get this "weird" situation:
user=> (loop [x (pos? 1)] (when-not x (recur (= 1 1))))
nil
user=> (loop [x (pos? 1)] (when-not x (recur true)))
Syntax error (IllegalArgumentException) compiling fn* at (REPL:1:1).
recur arg for primitive local: x is not matching primitive, had: java.lang.Boolean, needed: boolean
So even tho' one equals one is true, you get the boolean
primitive from =
not the Boolean
from true
š
user=> (def a (pos? 1))
#'user/a
user=> (loop [x a] (when-not x (recur true)))
nil
user=> (let [a (pos? 1)] (loop [x a] (when-not x (recur true))))
Syntax error (IllegalArgumentException) compiling fn* at (REPL:1:1).
recur arg for primitive local: x is not matching primitive, had: java.lang.Boolean, needed: boolean
In the second case, a
is inferred to be primitive, which allows x
to also be inferred to be primitive. In the first case, even tho' a
has the exact same value as the local a
, it gets promoted to a non-primitive when used for the local binding in the loop
.loop
/`let` try to infer primitive types for efficiency, but you have to be very careful to avoid promotion (to Boolean
, Long
, Double
-- boxing), so it can be a surprising area.
Clojure does its best to help you avoid losing that efficiency accidentally -- especially if you always set the reflection warning flag -- but sometimes it can be harder to get the primitive type than you might expect
user=> (def a (pos? 1))
#'user/a
user=> (loop [x ^boolean a] (when-not x (recur true)))
nil
Since there's no syntax error here, you can deduce that x
was not deduced to be primitive even with the attempt at type hinting...user=> (loop [x (boolean a)] (when-not x (recur true)))
Syntax error (IllegalArgumentException) compiling fn* at (REPL:1:1).
recur arg for primitive local: x is not matching primitive, had: java.lang.Boolean, needed: boolean
But that works as "expected".(`source` shows us that boolean
is an inline function calling clojure.lang.RT/booleanCast
which has a primitive return type, if look in Clojure's Java source code)
yep, those are documented here https://clojure.org/reference/java_interop#_coercions
Also, be careful when trying to add "primitive type hints" because they don't always do what you expect:
user=> (run! println [(meta ^long []) (meta ^int []) (meta ^boolean [])])
{:tag #object[clojure.core$long 0x1280851e clojure.core$long@1280851e]}
{:tag #object[clojure.core$int 0x5e840abf clojure.core$int@5e840abf]}
{:tag #object[clojure.core$boolean 0x35f8a9d3 clojure.core$boolean@35f8a9d3]}
nil
In this case long
, int
, and boolean
are resolved to the Clojure core functions of those names...
hey team, a friend asked me, about advice getting into clojure. Wanted to ask the community: do you have any project-based resources you'd recommend? Ideally it would be something, where each unit you build some cool project ā¢ beginning, you wouldn't need any deps, and project would be simple. only use is to get the repl up + dev env ā¢ then it gets more and more fun : ]
what's the pros and cons of src/first_part/second_part/core.cljs versus src/first_part/second_part.cljs ?
@ozzloy_clojurians_net Those would have namespaces first-part.second-part.core
and first-part.second-part
-- does "core" make sense in that context, for your project?
Pick meaningful names for namespaces (and files).
i'm not sure whether it's meaningful in this context. i'm creating a bingo web app to play with my family on sundays and decided to do it in clojure. this is my first clojure project, so i'm not sure what's idiomatic
The only reason core.cljs
seems so common is that Leiningen, long ago, decided to add .core
to the ns of any project created with just a single segment -- because there are some compatibility concerns with Java (about not having a top-level class without a package name).
I would favor the former.
Unless "core" is actually a meaningful concept in your program, it's a poor name.
When you work with the Clojure CLI / deps.edn
(instead of Leiningen) and you use clj-new
(my descendant of lein-new
), it prohibits single-segment names so you won't get .core
appended š
oh, and what about src/clj/whatever + src/cljs/whatever versus src/whatever/*.{clj,cljs,cljc} ?
It's simpler to just use src/whatever/foo.clj{s,c}
cool. that's what i was leaning towards too. do you know why the extra folder appears in some projects?
the whole src/{language}/whatever/*.*
thing is more of a Java/Maven practice
In the Clojure Contrib libs, you'll see src/main/whatever/*.clj
and src/test/whatever/*.clj
partly because they are all built with Maven.
In Clojure itself, you'll see src/clj/*
and src/jvm/*
for the Clojure source and the Java source.
oh yeah, i forgot about the testing thing. some projects do /src/bla + /test/bla, some do /src/bla and src/test/bla
i've also seen /test/bla_test. not sure what's up with the final "_test". do you know what that's about?
also, thanks so much for answering these questions! i have no idea how i'd find out other than talking to someone who's been in the community a while
My preference is src/whatever/foo.clj
and test/whatever/foo_test.clj
(with the namespace whatever.foo-test
).
The -test
convention is common to a lot of tools.
Cognitect's test runnner assumes it (but it can be overridden).
There's a cljs test runner as well (from olical I think).
And this runs all the tests -- both as ClojureScript and as Clojure (and in multiple Clojure versions) https://github.com/seancorfield/honeysql/blob/develop/run-tests.sh
Insofar as it provides a DSL (as data structures or via helper functions) that then generates SQL, yes it's a bit like Hiccup.
Here's a fragment of the test suite so you can see the SQL DSL https://github.com/seancorfield/honeysql/blob/develop/test/honeysql/core_test.cljc#L213-L217
i could see it possibly being meaningful for the Clojure source itself to have different folders for java and clojure source code. seems like it wouldn't really be meaningful in most projects though
i'll probably use honeysql in my project. thanks for these pointers. good to see some examples of testing too.
And next.jdbc
š
Hello. I would like do something like (map quote forms)
in the context of writing a Clojure to Go compiler for a school project. However:
(map quote '(a b c))
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: quote in this context
class clojure.lang.Compiler$CompilerException
i'm not exactly sure the specifics in clojure, but map usually takes a function, not a special form
@ruyvalle What is the actual input that you're starting with, and what are you trying to produce?
Mapping quote doesnāt seem particularly helpful as the forms will be evaluated for map. Quote would get the already evaluated forms
quote means "read but don't evaluate". if you already have forms, they've already been read or whatever
@ozzloy_clojurians_net that was helpful actually š
@seancorfield For what Iām doing right now my input is a Clojure string that has gone through read
, and my output is a Clojure form (in the form of a Cons
IIUC), which later gets transformed into Go code (a string).
I am currently trying to implement a primitive form of macros and am almost there I think. I was experimenting with using eval
and implementing if
as a macro in terms of cond
, but both branches were being evaluated during macroexpansion, hence why I wanted to quote stuff.
I ended up applying a substitution to each term in my form, which seems to work, except that syntax-quote
is read
rather oddly when the quoted form is, e.g., a function application.
For example I end up with
(clojure.core/seq
(clojure.core/concat
(clojure.core/list
(quote clojure.core/cond))
(clojure.core/list false)
(clojure.core/list (/ 1 0))
(clojure.core/list :else)
(clojure.core/list 2)))
as the expansion of (my-if false (/ 1 0) 2)
. Instead what I want is (clojure.core/cond false (/1 0) :else 2)
.also sorry if this is a little advanced for this channel š I started using Clojure only recently so I very much consider myself a beginner
hello all, how do I cast this?
(proxy [ExpectedCondition] []
(apply [^org.openqa.selenium.JavascriptExecutor d]
(clojure.pprint/print-table (:members (r/reflect d)))
(->
(.executeScript d "return document.readyState")
.toString
(.equals "complete"))))
it seems that the d
is not casting to JavascriptExecutor, why?google is failing me, and apparently I can't read the clojure docs. what's the idiomatic way to apply a sequence of fns to a value, e.g. (??? [f g h] x)
-> (h (g (f x)))
?
yeah, that'll work. seems like there should be a core library macro to do such a thing, but that'll get the job done. thanks --
I prefer v1. š
(-> x f g h)
thread first macro can do the same
unfortunately my fns are in a collection, not a known set of fns. that being said, writing a macro to expand into a threading macro would work also.
only if you need the apply-comp combination too frequently, otherwise its an overkill
how come clojurescript doesnāt have agents? that feels like a big pain for doing interop
Perhaps because JavaScript runtimes are single-threaded? Or at least are only multithreaded via webworkers, which are not shared memory threads the way JVM threads are.
ah right that makes sense, thanks
@U0CMVHBL2 I think it would be pretty easy to āemulateā agents even I single-threaded environment just to be compatible with clojure.core API surface. Am I missing some technical caveat in possible implementation there, or the idea was more like āitās probably a mistake to use agents in CLJS so we explicitly say you shouldnāt use themā?
@UTXR46DS6 If you mean implement functions like send
and send-off
by immediately executing the caller-supplied update functions f
before returning from send
/ send-off
, then sure, that is pretty easy to do.
One could also implement ref
and dosync
in a single-threaded runtime quite easily.
To what end?
Also, if you look at this ClojureScript documentation page, it mentions "Agents are currently not implemented" in the Concurrent Programming section, so it appears that at least whoever wrote that may have been thinking they might be in the future: https://clojurescript.org/about/differences
Thanks!
I'm using this BigDecimal library (https://github.com/iriscouch/bigdecimal.js#readme) and I get an infinite recursion error when I try to log one to the console. How can I override the behavior of those objects being printed so that it doesn't infinitely recurse? Come to think of it... this might be something with shadow-cljs and/or binaryage/devtools.
cljs$core$array_QMARK_ js/main/cljs-runtime/cljs.core.js:209
cljs$core$seq js/main/cljs-runtime/cljs.core.js:4747
2 js/main/cljs-runtime/cljs.core.js:17686
sval js/main/cljs-runtime/cljs.core.js:11590
cljs$core$ISeqable$_seq$arity$1 js/main/cljs-runtime/cljs.core.js:11741
cljs$core$seq js/main/cljs-runtime/cljs.core.js:4745
cljs$core$print_prefix_map js/main/cljs-runtime/cljs.core.js:33641
cljs$core$print_map js/main/cljs-runtime/cljs.core.js:33650
cljs$core$pr_writer_impl js/main/cljs-runtime/cljs.core.js:33015
cljs$core$pr_writer js/main/cljs-runtime/cljs.core.js:33120
print_prefix_map js/main/cljs-runtime/cljs.core.js:33640
cljs$core$pr_sequential_writer js/main/cljs-runtime/cljs.core.js:32809
cljs$core$print_prefix_map js/main/cljs-runtime/cljs.core.js:33629
cljs$core$print_map js/main/cljs-runtime/cljs.core.js:33650
cljs$core$pr_writer_impl js/main/cljs-runtime/cljs.core.js:33015
cljs$core$pr_writer js/main/cljs-runtime/cljs.core.js:33120
print_prefix_map js/main/cljs-runtime/cljs.core.js:33640
cljs$core$pr_sequential_writer js/main/cljs-runtime/cljs.core.js:32822
cljs$core$print_prefix_map js/main/cljs-runtime/cljs.core.js:33629
cljs$core$print_map js/main/cljs-runtime/cljs.core.js:33650
cljs$core$pr_writer_impl js/main/cljs-runtime/cljs.core.js:33015
cljs$core$pr_writer js/main/cljs-runtime/cljs.core.js:33120
print_prefix_map js/main/cljs-runtime/cljs.core.js:33640
cljs$core$pr_sequential_writer js/main/cljs-runtime/cljs.core.js:32809
cljs$core$print_prefix_map js/main/cljs-runtime/cljs.core.js:33629
cljs$core$print_map js/main/cljs-runtime/cljs.core.js:33650
cljs$core$pr_writer_impl js/main/cljs-runtime/cljs.core.js:33015
cljs$core$pr_writer js/main/cljs-runtime/cljs.core.js:33120
print_prefix_map js/main/cljs-runtime/cljs.core.js:33640
I turned off shadow's console support by adding {:console-support false} to the build, based on https://shadow-cljs.github.io/docs/UsersGuide.html#_preloads No joy.
From Tony Kay and Fulcro RAD:
#?(:cljs
(extend-protocol IPrintWithWriter
ty/TaggedValue (-pr-writer [d writer opts]
(let [t (.-tag d)
v (.-rep d)
type (case t
"f" "bigdec"
"n" "bigint"
"tagged")]
(-write writer (str "#" type " \"" v "\""))))))
hello, i have a bit of a noobish question.
Where is the definition of the if
special form.
If i wanted to write my own if
without the use of already provided conditionals (`when`, cond
, etc), how can i do so?
Special forms are built into the Clojure compiler so you'd have to go read the Java source code but the short answer is you can use a macro to write your own if
form.
You need a macro in this case, not a function, because you do not want the 2nd or 3rd expression evaluated (function calls evaluate their arguments, macros do not).
understood. i have been reading common-lisp (intro to symbolic computation) and i realize i need a macro.
So to write a if
macro, i would still need to use built in conditionals.
I see no other way of writing it.
(although I'm not sure how you'll write if
without if
since you need a conditional to test the truth of the test expression)
Given that only false
and nil
are falsey, yup, that's a good suggestion!
That only tests that a value is actually true
-- not just truthy.
Some things are so fundamental, they kind of have to be baked into the core language itself...
You can write your own versions of when
, cond
, etc but they'll all depend on the if
special form...
agreed. thank you very much for clarifying where the if
special form is defined.
with the built in if
i can write other conditionals using macors.