Fork me on GitHub
#clojure
<
2023-01-31
>
didibus02:01:15

I have code inside eval that works in my REPL, but fails inside cognitect test.runner. It says it can't find symbol var-scope, which is a macro I've got required in the test namespace. Is there something about eval not getting the context of the namespace it's called in?

phronmophobic02:01:53

it's possible that *ns* is different when run in the repl vs in the test

didibus02:01:15

Oh, actually I'm wrong about it being eval, it's Compiler/analyze that fails in the test runner, but succeeds in the REPL

Alex Miller (Clojure team)02:01:31

test-runner requires test namespaces but does not change ns

didibus03:01:17

It tries to resolve a symbol in the form it's analyzing, and can't find it. I guess that's a more esoteric use case for someone to use Compiler/analyze

hiredman03:01:50

As mentioned it is because in the repl the compile time and runtime value of *ns* is the same, but in arbitrary execution contexts there is no guarantee of any relationship between the compile time and runtime values of *ns*

hiredman03:01:36

Use syntax quote so your symbols are namespace qualified at read time

didibus03:01:47

Ya, looks like you're right, it's because Compiler/analyze is using the ns of where I have the function that calls it, not where I'm calling my function that ends up calling it

hiredman03:01:44

*ns* is compile time state, you shouldn't expect it to be any particular value at runtime unless you set it

didibus04:01:51

Well, now I get a Var$Unbound, but it's progress

escherize03:01:55

Any prior art for having clojure.test.check take “5 seconds” instead of “1000 iterations”?

Sam Ritchie04:01:38

this library did it back in the day… https://github.com/clojure/test.generative

til 2
hifumi12304:01:01

For generative tests, you can dynamically bind clojure.spec.alpha/*fspec-iterations* to a low number like 5.

dpsutton04:01:20

is the question about how to bind the cardinality of test cases by time rather than number?

2
Sam Ritchie04:01:34

Yeah, I think they’re asking to “run for 5 seconds” vs run for n iterations

jeroenvandijk06:01:17

Maybe if you create a clock in your application, 5 seconds could be 5000 ticks depending on the granularity. Manifold repo has an example of such a clock.

respatialized14:01:34

you could create a lazy sequence of generative tests with fmap and consume from it until a timer indicates 5 seconds have elapsed

respatialized15:01:34

set an atom to true and have it expire in 5 seconds with (future (swap! *stopped?* (fn [_] (Thread/sleep 5000) false) and use take-while, or just use loop

4
escherize15:01:43

I like that approach @UFTRLDZEW thanks.

didibus05:01:02

Anyone know if there is a better way than using Compiler/analyze to infer the primitive type of an expression? Can I use tools.analyzer for that instead?

Casey11:01:47

What's the most straightforward way to create a comparator that will work like compare in all situations, except that it compares strings case insensitively ?

jumar11:01:01

Make both strings lower case?

(sort compare ["Hello" "hello" "ahoj"])
("Hello" "ahoj" "hello")

(require '[clojure.string :as str])
(sort #(compare (str/lower-case %1) (str/lower-case %2)) 
            ["Hello" "hello" "ahoj"])
("ahoj" "Hello" "hello")

p-himik11:01:10

(defn my-compare [a b]
  (if (and (string? a) (string? b))
    (.compareToIgnoreCase a b)
    (compare a b)))

👍 4
Casey12:01:40

I've worked out more or less the same functions.. but these breakdown when combined with juxt for multi-field sorting. Hence the "in all situations".. For example:

(sort-by (juxt :name :phone)
         my-compare
         [{:name "aaron" :phone 0} {:name "Alice" :phone 1} {:name "Betty" :phone 2}])
;; => ({:name "Alice", :phone 1} {:name "Betty", :phone 2} {:name "aaron", :phone 0})
I'd expect the order to be the same as the input

Martin Půda12:01:45

In this example, my-compare receives two vectors of values, so you have to change them somehow. A wild guess:

(defn lc [o]
  (if (string? o)
    (str/lower-case o)
    o))

(defn my-compare [a b]
  (cond (and (string? a) (string? b))
        (.compareToIgnoreCase a b)
        (and (vector? a) (vector? b))
        (compare (mapv lc a)
                 (mapv lc b))
        :else (compare a b)))

p-himik12:01:35

@UK0810AQ2 That's exactly what I used in my first post in the thread. But it won't cut it because you can't make Clojure compare vectors of strings in a different manner without making your comparator vector-aware. Same goes for every other collection type that ends up comparing its elements pairwise.

👆 2
👍 2
Ben Sless12:01:31

ack, I see now

Ben Sless12:01:11

Seems like if you want a comprehensive solution you'll need to implement the entire tower with a protocol or multi method

jeroenvandijk13:01:16

What about something cruel like this?

(defn my-compare [a b]
  (.compareToIgnoreCase (pr-str a) (pr-str b)))

😱 10
Casey06:02:26

I've resorted to preprocessing the list of maps, by lowercasing strings

Túlio Abner de Lima21:01:13

Hello, can someone help me understand why this works:

(new org.json.JSONArray "[{\"foo\":1}]")
; #object[org.json.JSONArray 0x116d3d6a "[{\"foo\":1}]"]
but this doesn't works:
(new org.json.JSONArray (identity "[{\"foo\":1}]"))
; Execution error (JSONException) at org.json.JSONArray/<init> (JSONArray.java:225).
; JSONArray initial value should be a string or collection or array.
? p.s.: this works too:
(new org.json.JSONArray (str "[{\"foo\":1}]"))
; #object[org.json.JSONArray 0x6121d769 "[{\"foo\":1}]"]

ghadi21:01:39

turn on reflection warnings

(set! *warn-on-reflection* true)
and you should see a difference in the calls

ghadi21:01:10

in the first and third examples, the compiler knows which overload method signature to target https://stleary.github.io/JSON-java/org/json/JSONArray.html

Túlio Abner de Lima21:01:27

Thanks! I think I understand the problem now. The compiler doesn't know that the result of (identity ...) is a string. I fixed it with:

(new org.json.JSONArray ^String (identity "[{\"foo\":1}]"))

Túlio Abner de Lima21:01:30

But I couldn't see the difference with (set! *warn-on-reflection* true), the output was the same.

ghadi21:01:05

the middle call should print a warning

Túlio Abner de Lima21:01:01

I'm using Calva in VSCode. Should it appear in output.calva-repl?

ghadi21:01:56

I'm not familiar with where calva shows stuff, but yes it sounds like the REPL window should show that warning

ghadi21:01:18

unless maybe the compiler selected the "Object" signature

ghadi21:01:30

and didn't reflect at all

Túlio Abner de Lima21:01:00

I tried it in repl and nothing:

Clojure 1.11.1
user=> (set! *warn-on-reflection* true)
true
user=> (new org.json.JSONArray (identity "[{\"foo\":1}]"))
Execution error (JSONException) at org.json.JSONArray/<init> (JSONArray.java:225).
JSONArray initial value should be a string or collection or array.

ghadi21:01:28

then it must be that the compiler matched the Object signature

💡 2
ghadi21:01:36

and thus no warning

Túlio Abner de Lima00:02:26

Ow, ok, I understand now. Makes sense, thanks!