This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-01-05
Channels
- # announcements (2)
- # babashka (23)
- # beginners (67)
- # biff (4)
- # calva (19)
- # cider (6)
- # clj-kondo (40)
- # clj-yaml (14)
- # clojure (3)
- # clojure-austin (13)
- # clojure-europe (18)
- # clojure-nl (1)
- # clojure-norway (26)
- # clojure-uk (5)
- # clojurescript (42)
- # datascript (2)
- # datomic (6)
- # emacs (32)
- # graalvm (8)
- # humbleui (12)
- # hyperfiddle (13)
- # jobs (5)
- # lambdaisland (1)
- # lsp (18)
- # malli (15)
- # off-topic (20)
- # overtone (1)
- # pathom (5)
- # pedestal (15)
- # portal (3)
- # reitit (13)
- # releases (1)
- # remote-jobs (1)
- # yamlscript (4)
I always mix up the default clause syntax of case
vs cond
. with cond
the default case is indicated by :else
whereas with case
the default case is indicated by a missing object-to-match
cond
always takes a predicate. people use :else
because it is truthy. But it’s always even pairs with predicate clause
> Takes a set of test/expr pairs. It evaluates each test one at a > time. If a test returns logical true, cond evaluates and returns > the value of the corresponding expr and doesn’t evaluate any of the > other tests or exprs. (cond) returns nil.
cond couldn’t be simpler: takes pairs, goes through them evaluating test for the first one that returns truthy
I suppose cond
could be a tiny bit simpler. If you omit the :else
place holder, cond
could still try to evaluate the test clause, and simply return its value if there's no consequence clause.
Then case
and cond
would be consistent
in Common Lisp the consequence clause is optional. if it is missing the value of the test expression is returned. emacs lisp cond
works the same way as Common Lisp cond
I once implemented cl-cond
in clojure. of course I don't use it because it would not be idiomatic
(cond
(even? x) :even
:else :odd)
is clojure vs something like
(cond
((even? x) :even)
(:odd))
yes, so does elisp. however, clojure's cond could have been implemented so that the final consequent were optional.
similar to case
oh well, that's water under the bridge
(cond
(test-1) (consequent-1)
(test-2) (consequent-2)
(consequent-3))
anyway every time I come back to clojure after being away for several months, I have to re-look-up the syntax for case
and cond
you can always add your own syntax:
(defmacro cl-cond [& body]
(let [body' (partition-all 2 body)]
(if (= 1 (count (last body')))
`(or
(cond ~@(butlast body))
~(last body))
`(cond ~@body))))
(cl-cond
(= 1 2) 42
(= 1 1) 43
44)
;; => 43
(cl-cond
(= 1 2) 42
(= 1 3) 43
44)
;; => 44
(cl-cond
(= 1 2) 42
(= 1 3) 43)
;; => nil
(cl-cond
(= 1 2) 42
(= 1 1) 43)
;; => 43
It seems to me that clojure.core/cond
behavior still could be changed to accept an odd number of arguments in a future version.
i think it's a rite of passage to write your own version of cond
lol, cuz i also did that
I should note that stylistically, I always add an ; else
comment before the final branch of my case
and condp
forms. I like to have the visual consistency
you could also do this:
(case kw
:a (foo)
:b (bar)
#_else (throw ...))
if it better matches your style.comment: clojure future
is remarkably easy to use compared to what hoops I have to jump through in Python.
even if using threading module? I didn't touch python for a while but I recall something similar as future could be done like that:
>>> import time
>>> from threading import Thread
>>> def foo():
... time.sleep(2)
... print("DONE")
...
>>> t = Thread(target=foo, args=[])
>>> t.start()
>>> DONE
>>>
I asked on Python discord, and nobody suggested that. I searched a bit on stackexchange and found several interfaces, which didn't seem to work recursively. I played with it for a few hours and finally gave up. Someone gave me so code that works, but I don't understand it.
Someone claimed that nothing as easy as future
in clojure exists in python because the model is very different, and doesnt assume the jvm.
does future
work so easily in JS clojure?
Here is the python code I'll show my students. I'm a bit nervous because I don't really understand it.
import asyncio
async def sum_list(data, op):
if len(data) == 1:
print(f"singleton: {data[0]=}")
return data[0]
else:
print(f"summing: {len(data)} {data=}")
mid = len(data) // 2
computed = await asyncio.gather(sum_list(data[0:mid], op),
sum_list(data[mid:], op))
return op(computed[0], computed[1])
if __name__ == "__main__":
print(asyncio.run(sum_list(range(12), lambda a,b: a+b)))
> Someone claimed that nothing as easy as future
in clojure exists in python because the model is very different, and doesnt assume the jvm.
I don't know about what model they are talking about but in clojure future
creates a thread and runs some function in it. Because python has threads and HOFs it can do just the same
some of the examples I tried from stackexchange forced the programmer to set up a thread manager object (forget the actual term used), but it was not clear whether I could recursively create such objects, and whether I could create threads within threads within threads. In my experimentation I got what looked to me like deadlocks, which I never figured out.
is seems to me that there should be something as easy. but clojure's model is d*mn easy.
what model?
model: just call future
and dereference it sometime later with @
and let the language manage the queue
another problem was that the python examples used side effects.. it wasn't clear how to manage return values. that probably would not have been difficult to figure out, except that i was having deadlocks for no obvious reason
question: some of the python examples made the programmer care about how many cpus are available. Is that something the programmer should be concerned about?
in my opinion, even if i have several cpus, I don't know whether other users or other programms (such as my IDE) are using them, and if I'm calling a function I didn't write, I don't know who many cpus that function is going to try to use.
what's the best way to test whether a sequence is a singleton? counting might fail if it is infinite, and taking the first element might fail because the first element might be false.
perfect
(defn bounded-count
"If coll is counted? returns its count, else will count at most the first n
elements of coll using its seq"
{:added "1.9"}
[n coll]
(if (counted? coll)
(count coll)
(loop [i 0 s (seq coll)]
(if (and s (< i n))
(recur (inc i) (next s))
i))))
does what you want. If it can O(1) get the count, use that, otherwise iterate through it with a bound
I'm guessing this performs better than something like:
(= 1 (count (take 2 coll)))
Or is there some other reason that wouldn't work?
@U04RG9F8UJZ that would be a fine way to do it but it is missing the obvious trick: if something knows it’s count (like a vector, a string, etc) we don’t have to iterate through it, we can just grab the number
Hey all, so I think this may be a a reduce fn, but how would you combine a vector of 2 vectors
[[:a :b] [:c :d]] [[:e :f] [:g :h]] such that the result was [:a ::b :e :f] [:c :d :g :h]
… can be up to a few dozen vector vectors```could use (juxt first second)
to split up the pairs, and then whenever you mash them back together you can use flatten
/`flatmap`?
ok, so even better
(->> my-vec (apply interleave) (partition-all 2))
(let [a [[:a :b] [:c :d]]
b [[:e :f] [:g :h]]
c [[:i :j] [:k :l]]]
(into [] (comp (partition-all 3) (map flatten))
(interleave a b c)))
;; [(:a :b :e :f :i :j) (:c :d :g :h :k :l)]
Something similarthe timing 😄
Thanks for all the advice, I knew there were some core fns that could make this process less brain-bending
my solution actually needs one more step
(->> my-vec
(apply interleave)
(partition-all 2)
(map flatten))
:^) 🤙, got you, benefit of preferring to have '100 functions that work on 1 datastructure' over '10 functions that work on 10 datastructures': we got all the tools in the world to manipulate sequences
Working with an API with PersonID and LocationID, and since Person 1:N Locations, you need to repeat the PersonID to positionally match the second argument, e.g. ?person-id=1,1,2,2,3,4,5&location-id=1,2,3,4,5,6,7 … they are monsters… that oughta be illegal lol
I mean on the bright side I can unironically say I actually needed to use (take n (repeat x))…
map
can take any number of arguments
(map concat [[:a :b] [:c :d]] [[:e :f] [:g :h]])
;; => ((:a :b :e :f) (:c :d :g :h))
Ah, he beat me to it!
user=> (let [x [[[:a :b] [:c :d]] [[:e :f] [:g :h]]]] (apply map concat x))
((:a :b :e :f) (:c :d :g :h))