Fork me on GitHub
#clojure
<
2022-05-11
>
Oliver George06:05:33

Hello. I'm trying to understand this error message class clojure.lang.PersistentVector cannot be cast to class clojure.test.check.rose_tree.RoseTree (clojure.lang.PersistentVector is in unnamed module of loader 'app'; clojure.test.check.rose_tree.RoseTree is in unnamed module of loader clojure.lang.DynamicClassLoader @7d5508e0) For context, I'm trying to get this going: http://blog.guillermowinkler.com/blog/2015/04/12/verifying-state-machine-behavior-using-test-dot-check/. There's a repo which works here https://github.com/guilespi/fsm-test-check/ But when I update the deps (clojure 1.6.0-> 1.11.1 & test check 0.7.0->1.1.1) I get the error.

seancorfield06:05:31

Probably best not to cross-post Qs between channels. You got an answer in #test-check, you just needed to be a bit more patient than eight minutes!

Stel Abrego07:05:39

Today I ran into this challenge of trying to find the first string matching a regex in a deeply nested Hiccup vector. This is what I came up with, it's pretty concise but I'd rather short-circuit and stop searching when I find the string. I tried loop but I had trouble replicating prewalk. What's the idiomatic way to walk a Hiccup tree to search for a single value?

(defn find-str-in-hiccup [hiccup]
  (let [result (atom nil)
        regex #"foobar"
        sniff-node #(cond
                     @result nil
                     (vector? %) %
                     (and (string? %) (re-find regex %)) (reset! result %)
                     :else nil)]
    (walk/prewalk sniff-node hiccup)
    @result))

p-himik08:05:29

Can that string be inside an attribute map?

jcf08:05:52

Have you tried a version with reduce and reduced? https://clojuredocs.org/clojure.core/reduced

p-himik08:05:49

I can't imagine how that would be simpler than the above. I'd use flatten here with a filter on top if maps shouldn't be checked for strings. And tree-seq if they should be.

Stel Abrego08:05:01

@U2FRKM4TW No it's not in an attribute map so I don't want to recurse into them. It's a direct child of a hiccup vector. And I'm assuming there's only one match in the whole tree. I just realized I could make a couple small optimizations to what I have and I edited the function.

p-himik08:05:49

Yeah, just make your own version of find-first in a sequence, and then flatten + filter for strings + find-first.

Stel Abrego08:05:52

@U2FRKM4TW Oh wow I didn't realize I could use flatten 🤯

jcf08:05:43

My two cents… Reduce avoids lazy sequences and can terminate early. You might find it performs better in terms of both space and time. By avoiding stateful constructs like atoms the solution is simpler. It might not be easier to code but that gets us into simple vs. easy. A little imagination goes a long way. Jumping into a hammock and thinking a little more can yield better results. Without all the information it's hard to say but having people willing to help is awesome. It's always exciting finding out something new about Clojure’s core functions. There's lots of good stuff ready to go, which makes me happy. I'm glad you found out about this awesome feature, @U019RSW97UZ. 💪

👍 2
Stel Abrego17:05:09

@U06FTAZV3 Oh dang I didn't realize we had a function like reduced, that's seriously cool. Double mind blown 🤯😄

❤️ 2
Stel Abrego22:05:57

A little update, I got a reduce/`reduced` version working and wow it's clean af. Thanks @U06FTAZV3!

(def some-hiccup
  [:div
   {:id "baz"}
   [:p "foobar"]
   [:section]
   [:section {:class "test"} "foo"]
   [:section [:p "foobar"]]
   "bar"])

(defn search-for-str [hiccup]
  (reduce
   (fn [_ item]
     (let [desc-result (and (vector? item) (search-for-str item))]
       (or (and (string? item) (re-find #"foobar" item) (reduced item))
           (and desc-result (reduced desc-result)))))
   hiccup))

(comment (search-for-str some-hiccup))

❤️ 1
🤌 2
pinkfrog11:05:39

I would like to run some code only once inside a clojure function. A brutal approach would be

(defn func []
  (defonce _once (some-once-code)
  (normal-code))
Any more sane approach? Note that I’d like the some-once-code to be totally encapsulated inside the func, and do not write it else where.

magnars11:05:18

Maybe this?

(let [locked? (atom nil)]
  (defn func []
    (when-not @locked?
      (reset! locked true)
      (some-once-code))
    (normal-code)))

pinkfrog11:05:16

delay might do it as every time a new delay obj is created.

pinkfrog11:05:19

might not do.

pinkfrog11:05:01

core/exists? tests if a name exists, can I create a name under an ns without using def , clj-kondo by default warns on inline-def.

Ben Sless11:05:27

You can put the delay outside, e.g.

(defonce foo (delay (once...)))
(defn func [] @foo (normal code))

👍 1
practicalli-johnny12:05:42

I am curious as to why it's desirable to contain the "some-once-code" within another function Otherwise defonce by itself seems acceptable

(defonce static-value (some-once-code)
(defn func []
  (normal-code))
Even a def is cached, so technically only runs once unless the developer expressly evaluates the def expression

p-himik12:05:59

> unless the developer expressly evaluates the def expression Or unless the namespace is reloaded.

pinkfrog12:05:12

> I am curious as to why it’s desirable to contain the “some-once-code” within another function Because the logic belongs within the enclosing function, so I want to make it more self-contained. And it also depends on the enclosing functions input parameters, so in that case, put inside the enclosing function is a must. It’s like the static declarator in c++.

p-himik12:05:14

There's also memoize. You can come up with a bunch of ways of how to use some of all those things together to achieve what you need.

pinkfrog12:05:15

@U2FRKM4TW Neither satisfy the initial requirement. The closest is what magnars posted.

p-himik13:05:30

If you decide to go with that, don't use deref+reset. Instead, use a single swap.

thanks3 1
pinkfrog13:05:30

compare-and-set! might be more direct.

👍 2
emccue13:05:41

(let [init (delay (println "once"))]
  (defn thing []
    (deref init)
    (other-code)))

p-himik13:05:22

> it also depends on the enclosing functions input parameters

emccue13:05:29

oh missed that

didibus23:05:32

Its pretty weird to want to do that, sounds like you're implementing a poor's man object oriented system?

emccue23:05:45

If you want a scoundrels object oriented system, boy do I have a 2 year old gist oozing at the seams

👹 1
didibus23:05:35

Anyways, I would use a promise personally:

(let [p (promise)]
  (defn foo
    [a b]
    (deliver p <code-to-only-run-once>)
    (do-thing-with @p)))
Though I guess maybe this is overkill, since the multi-threaded machinery isn't needed in a local context, so maybe using volatile! is better from a performance standpoint:
(let [v (volatile! nil)]
  (defn foo
    [a b]
    (when (nil? @v) (vreset! v <code-to-only-run-once>))
    (do-thing-with @v)))
Hum, oh I assumed you wanted to cache a value as well, if its just like side-effect you can just make it a flag:
(let [ran (volatile! false)]
  (defn foo
    [a b]
    (when-not @ran
      <code-to-only-run-once>
      (vreset! ran true))
    (do-other-things)))
I think also a more functional way might be to return a function. Or maybe its even more OO I don't know anymore 😛
(defn make-some-fn
  [a b]
  <code-to-run-first-time>
  (fn [a b]
    <function to run thereafter>))
Though the caller now needs to cache the returned function and subsequently call that, a bit like how you'd create an object with some initial state, and later use that object... damn so I think this is even more like OO. And at that point, maybe you should use a deftype + protocol + constructor function, or a defrecord with the same:
(defprotocol PFoo (do-somthin [a b]))
(defrecord Foo [some state]
  PFoo
  (do-somethin [a b] <wtv-you-want>))
(defn make-foo [a b]
  <do-something-once-at-construction-with-a-b>  
  (->Foo a b) ;; assuming a b is state you need afterwards
)
And if you suffer from OCD and feel this is not "self-contained" enough for you, go full-java and shove it in its own namespace:
(ns Foo)
(defprotocol PFoo (do-somthin [a b]))
(defrecord Foo [some state]
  PFoo
  (do-somethin [a b] <wtv-you-want>))
(defn make-foo [a b]
  <do-something-once-at-construction-with-a-b>  
  (->Foo a b) ;; assuming a b is state you need afterwards
)
Or wrap it all inside a do:
(do
  (defprotocol PFoo (do-somthin [a b]))
  (defrecord Foo [some state]
    PFoo
    (do-somethin [a b] <wtv-you-want>))
  (defn make-foo [a b]
    <do-something-once-at-construction-with-a-b>  
    (->Foo a b) ;; assuming a b is state you need afterwards
  )
)

didibus00:05:47

Oups, actually nevermind, you do need to deal with concurrent calls to the function, and the variable is actually shared between calls, so promise would work, or like you said, compare-and-set! with an atom. Hum... actually compare and set might not be thread safe here either, since it's not blocking. Like it wouldn't re-run the code, but if another function is running shouldn't it wait for that code to have ran? If so, promise really seem the right way to go.

thanks3 1
pinkfrog13:05:10

I have a question on data shape. I put into the following code.

;; `m` is in the shape: {:a [ {:x [some intergers]} 
  ;;                            {:x [another some integers] 
  ;;                            ,,, }

  ;; I can append a number to [some integers] with the following code.
  ;; For example, append 6 to the 1st [numbers] list.
  (let [m {:a [{:x [1 2 3]}
               {:x [4 5]}]}]
    (update-in m [:a 1 :x] (fnil conj []) 6))
  
  ;; what if `m` is initially `{}`, or `{:a []}` ? 
  ;; (update-in m [:a 0 :x] (fnil conj []) 6))  simply does not work

delaguardo13:05:42

it does work. maybe you expect different result?

pinkfrog13:05:04

I’d expect it to be {:a [{:x [6]}]}

delaguardo13:05:36

update-in does not assume that integer in path means vector. It will always create a hash map if not present in m

delaguardo13:05:01

You can find this in the docstring

ghadi13:05:22

code that updates is usually distinct from code that initializes

pinkfrog13:05:37

Yes. I am aware of that. I just don’t know how to achieve the desired result.

pinkfrog13:05:52

I am fine with specter or other tool (if applicable).

pinkfrog13:05:49

if-else-ing on the data structure to check if it already contains a vector is, , might be little error prone and verbose.

delaguardo13:05:17

You can write your own version of update-in

1
didibus06:05:54

Think this would do it:

(defn update-inv
  [m ks f & args]
  (if-let [k (first ks)]
    (if (next ks)
      (update m k (fn [old] (if (or (nil? old) (empty? old))
                              (if (number? k)
                                (apply update-inv (into [] (repeat k (f nil))) (next ks) f args)
                                (apply update-inv {} (next ks) f args))
                              (apply update-inv old (next ks) f args))))
      (if (or (nil? m) (empty? m))
        (if (number? k)
          (apply update (into [] (repeat k (f nil))) k f args)
          (apply update {} k f args))
        (apply update m k f args)))
    m))

pinkfrog07:05:00

Haha. Using my clojure power to understand it.