This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # announcements (2)
- # asami (1)
- # babashka (15)
- # beginners (29)
- # calva (6)
- # cider (22)
- # clara (1)
- # cljfx (1)
- # clojure (86)
- # clojure-australia (1)
- # clojure-dev (25)
- # clojure-europe (38)
- # clojure-france (7)
- # clojure-italy (3)
- # clojure-losangeles (1)
- # clojure-nl (6)
- # clojure-seattle (1)
- # clojure-spec (9)
- # clojure-uk (25)
- # clojuredesign-podcast (2)
- # clojurescript (4)
- # conjure (9)
- # cryogen (8)
- # cursive (17)
- # data-science (5)
- # datalog (1)
- # deps-new (1)
- # depstar (1)
- # emacs (1)
- # events (6)
- # fulcro (2)
- # graphql (3)
- # helix (4)
- # jackdaw (1)
- # jobs (1)
- # kaocha (9)
- # leiningen (4)
- # malli (1)
- # off-topic (7)
- # other-lisps (2)
- # pathom (12)
- # re-frame (16)
- # remote-jobs (1)
- # shadow-cljs (28)
- # spacemacs (19)
- # sql (2)
- # test200 (2)
- # testing (5)
- # timbre (1)
- # tools-deps (18)
- # xtdb (10)
isn't there a
funcall function in clojure? I know it's not absolutely necessary, but sometimes it can make the code more readable.
For example, this call to
reduce returns a function that needs to be called with no arguments.
It works if I just wrap this in a second set of parens.
(reduce (fn ( default-f) ([_ [keys f]] (if (exists [x keys] (test value x)) (reduced f) default-f))) default-f pairs)
But I think it is more readable if I use funcall to emphasize what's happening.
((reduce (fn ( default-f) ([_ [keys f]] (if (exists [x keys] (test value x)) (reduced f) default-f))) default-f pairs))
(funcall (reduce (fn ( default-f) ([_ [keys f]] (if (exists [x keys] (test value x)) (reduced f) default-f))) default-f pairs))
(.invoke (reduce (fn [f k] (fn  (str (f) (k)))) [(fn  "q") (fn  "w") (fn  "e")]))
Another way to emphasize this is to use
let to bind the return value of the
(reduce ...) expression to some name, then in the body of the
letcall that function, e.g.:
(let [f (reduce …)] (f))
@U0CMVHBL2 yes that's a good idea as well. I ended up just implementing a
funcall function in my local utils library. Then after that I ended up removing the code altogehter, I think it is a bit clearer using loop/recur,
(defn -casep-helper [test value default-f & pairs] (loop [pairs pairs] (cond (empty? pairs) (default-f) :else (let [[keys f] (first pairs)] (if (exists [x keys] (test value x)) (f) (recur (rest pairs)))))))
@U0CMVHBL2, yes as i mentioned earlier, you're right. I can just wrap an extra set of parens around it and it works perfectly well. I just find that the explicit
funcall makes it easier to understand what's happening.
I originally had an error in this function and it made an infinite loop. Can you spot the error?
The error was what motivated me to try to rewrite it using
(defn -casep-helper [test value default-f & pairs] (loop [pair pairs] (cond (empty? pairs) (default-f) :else (let [[keys f] (first pairs)] (if (exists [x keys] (test value x)) (f) (recur (rest pairs)))))))
It is not obvious to me from a minute of reading where that function would cause an infinite loop, unless the infinite loop is in
pairs inside the loop, whereas you probably should be using the (misnamed since it's still a seq)
pair I think.
So an infinite loop happens because recuring with
(loop [[pair & more] pairs] (if pair (do ... (recur more)) ...))
(rest pairs)presumably always has some values left, since it's what you passed into the function, not the loop
The infinite loop is that I meant to use
(loop [pairs pairs] ...) but instead i used
(loop [pair pairs] ...) ... That's when I said to myself, what I really want to do is iterate over a sequence until some condition, which is exactly what
reduce/`reduced` is for.
but the conversion to
reduce didn't aid in readability, in my opinion for two reasons. 1. in order to prevent the
default-f function from being called unnecessarily was tricky, and 2. handling the empty sequence, reduce calls the given function with 0 arguments in case of an empty sequence, so I had to provide a multiple-arity function, which further obfuscates the code.
In the end, I think that
loop/`recur` is probably clearer.
w.r.t. the following suggestion
I really don't like that coding style, even though I know it is very often used in the clojure community. One reason I don't like it is that it works accidentally. The code would fail if there is every a pair such as
(loop [[pair & more] pairs] (if pair (do ... (recur more)) ...))
(false not-false)in the list of
pairs. I avoid this accidental dependence in my code when I can help it, even if that makes for someone non-ideomatic clojure coding style.
A pair of false and true is still truthy. Admittedly the code doesn't hit everything if your list of pairs can contain nils. Either way I would prefer reduce to loop when it's feasible, I was just trying to help point out where the infinite loop came in and what the form of
(loop [[pair & more] [ [false true] ]] (if pair (do (println pair) (recur more)) nil)) [false true] => nil
loopbindings is 🤷
(defn -casep-helper [test value default-f & pairs] @(reduce (fn [agg [keys f]] (if (exists [x keys] (test value x)) (reduced (delay (f))) agg)) (delay (default-f)) pairs))
@ULNRSUK8C, you're right about a pair of
[false non-false] still being truthy. I guess I was thinking about adding the pair destructuring also. such as
I could try something like this:
(loop [[[key value] & pairs] pairs] (if key ...
But I must admit that bothers me because for me, it still works accidentally. OTOH, i'm, not completely comfortable using
(loop [[[key value :as pair] & pairs] pairs] (if pair ...
:asin because it works sometimes but not others, and I don't remember the cases where it's legal. For example, I know the following is illegal.
and this is illegal as well
(defn foo [a & as :as all] ...)
(defmacro foo [a & as :as all] ...)
you have to have :as inside a destructure form I believe, so this works
(defn foo [& [a & as :as all]] (println a) (println as) (println all)) => #'user/foo (foo 1 2 3 4) 1 (2 3 4) (1 2 3 4) => nil
:as couldn't know whether to refer to a and as or just as or nothing
I'm not completely sure what you mean when you say it works accidentally, if you can't rely on pairs being a list of pairs then I'd suggest either the call point is wrong or you need another function that does validation first.
I guess the following isn't so bad.
(apply (reduce (fn ( default-f) ([_ [keys f]] (if (exists [x keys] (test value x)) (reduced f) default-f))) default-f pairs) ())
reduce won't use the 0 arg arity of that function - supplying default-f as the init arg already accomplishes the right thing
Clojure is a Lisp-1, not a Lisp-2 like Common Lisp, so you can use functions in call position, or even any other expression that, when evaluated, returns a function, e.g.
user=> ((constantly true) 5) true
In this, Clojure is more like Scheme than Common Lisp. I don't recall whether Scheme has
funcall, but I would guess no.
Hi. I have a clojure test (deftest) running fine at the REPL, but that fails when I run using deps.edn (clj -M:test) with cognitect test runner, because “class not found”. Fixed if I manually compile AOT before running the test. So… is it possible to automate AOT for tests without resorting to a Makefile? Any pointers gratefully received! Thank you
@mark354 Yes, you can add
"-e" "(compile,'the.namespace)" into your
:main-opts for running the tests, ahead of the
"-m" option for the test runner. See https://github.com/seancorfield/cfml-interop/blob/develop/deps.edn#L11-L13 for an example of this.