This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-10-04
Channels
- # aleph (10)
- # announcements (2)
- # babashka (2)
- # beginners (101)
- # calva (17)
- # cider (11)
- # clara (6)
- # clj-kondo (25)
- # cljsrn (33)
- # clojure (181)
- # clojure-dev (15)
- # clojure-europe (3)
- # clojure-italy (4)
- # clojure-nl (8)
- # clojure-uk (22)
- # clojurescript (111)
- # clojutre (58)
- # cursive (31)
- # data-science (1)
- # datomic (10)
- # emacs (6)
- # ethereum (1)
- # fulcro (20)
- # graalvm (3)
- # jackdaw (5)
- # leiningen (5)
- # off-topic (31)
- # re-frame (2)
- # reitit (10)
- # shadow-cljs (9)
- # spacemacs (16)
- # sql (8)
- # tools-deps (16)
- # vim (17)
what is more efficient :`(async/<!! (clojure.core.async/timeout` or some thread sleep ?
with a/<!!
you block the current thread anyway so I guess it doesn't matter: if you just want simple sleep I would use Thread/sleep
Hello Clojurians,
I came across a somewhat odd behavior for apply
(if I’ve identified correctly) and the behavior of function encapsulated apply
seems to be a bit different then standalone
Below are the function definition and the state-map
(def mutation-env {:ref [:person/id 1]
:state (atom {:person/id {1
{:person/id 1 :person/name "Dad"}}})})
(defn update-caller!
[{:keys [state ref] :as mutation-env} & args]
(apply swap! state update-in ref args))
Here’s the different response wrt the string Mom
.
(update-caller! mutation-env
assoc :person/name "Mom")
;; => #:person{:id {1 #:person{:id 1, :name "Mom"}}}
(apply swap! (:state mutation-env) update-in (:ref mutation-env)
assoc :person/name "Mom")
;;=> #:person{:id {1 {:person/id 1, :person/name \M, \o \m}}}
Am I missing some detail here, please let me know 🙂The body of update-caller!
in your first call to it with the arguments substituted is equivalent to this:
(apply swap! (:state mutation-env) update-in (:ref mutation-env) [assoc :person/name "Mom"])
As Michiel pointed out, a minimal repro helps.
(apply + 1 [2 3])
is equivalent to (+ 1 2 3)
And you've done something like (defn my-add [a & args] (apply + a args))
, and called it like this, (my-add 1 2 3)
. In the body of my-add
, args
is bound to [2 3], which apply
splices in the call to + making it equivalent to (+ 1 2 3)
and you get the same result.
Sorry if the explanation is unclear. Am unable to articulate it any more clearly 😅
jalhindhreddy explained but here are some more examples:
& args
collects additional args into an array.
apply
unpacks its last arg as additional arguments.
Let's (defn f [& a] a)
, similar to vector
.
Then (f 1 2)
-> [1 2]
(apply f 1 [2 3])
-> [1 2 3]
(apply f 1 ["Mom"])
-> [1 "Mom"]
(apply f 1 "Mom")
-> [1 \M \o \m]
because strings are seq
s of characters
Thanks @U883WCP5Z and @U9MDWLP5Y, now I have understood the reason. Regarding the minimal example, hmm, that’s a skill which is still elusive to me 😅 I do think this involves being able to deconstruct the problem already - well, getting there for sure
It's easiest by trial and error, simply hack bits off until it no longer fails / runs, then put the last thing back and try hacking something else off
This might be of interest: https://www.youtube.com/watch?v=FihU5JxmnBg On debugging in general. Lots of interesting reference points mentioned in the talk...
Thanks @U9MDWLP5Y - will keep this in mind!
@U883WCP5Z cool this seems useful - learning to actually use a debugger in clojure in a priority for sure.
In update-caller you're calling apply with a sequence of args as the last arg. In the bottom example you're providing them as separate args, not wrapped in a seq
Ohh, got it - thanks!
Now, I realized that the args
in the defn
gets in as a seq in this case.
it often helps to bring these examples down to smaller repros to understand what's going on
Is it better to return a vector/list from a function instead of a set? If I return a set should I name the variable to reflect that the element binded is a set?
@mario.cordova.862 "It Depends". If returning a set makes more sense, do that. If returning a vector makes more sense, do that. If returning a general sequence makes more sense, do that.
Do callers care about the order of results? How will they use the result?
And regarding your question about advice on naming things, I still need to read, but have heard good reviews of, the book "Elements of Clojure" https://leanpub.com/elementsofclojure

Yep, the first two chapters are relatively clear and practical. The first chapter is mostly useful for some naming things. As regards to @mario.cordova.862’s question I don't think it's a generally used convention to capture return type in the name - use spec
and/or document it in docstring.
The last line in the function is doing a set intersection but it makes more sense to just return a list but I was just returning the set as is. Wondering if it was best practice to turn the set into a list
Vectors are Counted and Indexed. Sets are Counted but not Indexed. Vectors are associative on their indices. Sets are not associative but can be used as predicates (they're "sort of" associative on their elements).
A set can be traversed using all of Clojure's sequence-based functions, just like a vector, map, or list can, so there is no harm in returning it as a set if the order does not matter, if the callers expect it to be a sequence.
Note that long term, if this function is used by other people on a team, or around the world, some of them may notice that it is a set, and choose to write code that depends upon that fact.
But I do not want to place unnecessary worry on your shoulders -- just an observation that others have noticed in software development, too. Just remembered that some people call that Hyrum's Law: https://www.hyrumslaw.com/
Got it! Another thing is that I am using that result in another function and doing some set manipulation with it as well. But I can see how someone who is looking at this code might not realize its a set. So my thinking was returning a list and then in the other function explicitly turning that list into a set so its obvious what is happening
As an efficiency note, it seems better to leave it a set, for your own use. If others only need its elements as a sequence, they can traverse it just fine whether it is a set or list, as mentioned above.
But the efficiency note might not be very important if your code that would turn it back into a set internally isn't in the 'hot path' of your application.
hmm in that case I need to keep it as a set then. Ill probably just some quick notes as comments
If some callers (like the use in the other function) require a set then I would document that it returns a set and maybe even add a spec for :ret set?
🙂
hi, I have a question about multi-methods. consider the following code:
(defmulti test (fn [m] (:type m)))
(defmethod test :type1
[m]
(+ (:price m) (:fine m)))
(defmethod test :type2
[m]
(- (:price m) (:discount m)))
I find very convenient to use a map
key to dispatch over defmethods, however when I am actually using the test
function above, the keys necessary to compute the operation may vary and I find it difficult to know that beforehand. In this example the map would need a key called fine
*or* discount
depending on the dispatch paththere is anything (other than not using multimethods here) that could help this situation?
The function you use in the defmulti
definition can be as long and complex as you want. It is not restricted in any way by Clojure. Realize that it will be run on every call of the multimethod, though, so could become a performance issue if it takes a long time to compute.
I am not understanding your suggestion. My question is almost about how to document better what the dispatched methods expected to receive inside the map
sorry, I misunderstood the point of the question, then.
If I understand it correctly now, the issue you are describing can occur with normal Clojure functions, too, not only multimethods.
(test {:type :type1 :price 20 :fine 1})
or (test {:type :type2 :price 20 :discount 5})
both are options of the same function called test
to the enduser. However, the user knows that :type2
has a different behavior, but how they will know what keys the input map should have? The only way would be to inspect the code and see that you are using :discount
there
The two primary techniques I know of for this are: documentation, and in some cases, clojure spec might be able to help
Is your goal to educate programmers who want to call the multimethod before they write code? To throw an exception if they call it incorrectly? Both?
Your defmulti
could dispatch on the presence of :fine
/`:discount` if that makes more sense than adding a synthetic :type
key. Hard to tell what's "best" based on just a short example like that though.
Hello, all! I am doing my best to learn Clojure, as my buddy and I are starting a project together. I am writing code as I am learning and I'm running into a bit of tight spot. (defn match-remove [t-words i-words result]
(let [[t-word & t-rest] t-words]
(if (blank? (str t-word))
result
(do
(conj result (str (map (fn [xs]
(for [x xs]
(if (= x t-word)
nil
t-word))) i-words)))
(match-remove t-rest i-words result)))))
That did not work as expected. Sorry I am new to slack.
(defn match-remove [t-words i-words result]
(let [[t-word & t-rest] t-words]
(if (blank? (str t-word))
result
(do
(conj result (str (map (fn [xs]
(for [x xs]
(if (= x t-word)
nil
t-word))) i-words)))
(match-remove t-rest i-words result)))))
this is the function I am working on, currently it only produces a empty vector
Inside the do
you are evaluating two expressions and throwing away the first one.
Clojure's conj
has no side-effects: it produces a new value, that (in this case) is result
with another string added -- but result
is unchanged.
This is Clojure's immutable data structures at work.
Ah, yes. Immutability
Then I misunderstood conj entirely
What you perhaps want here is
(let [result' (conj result (,,,))]
(match-remove t-rest i-words result'))
If you come from a statement-based language, it's common to trip up on do
and implicitly assume side-effects in a series of expressions.
Thank you for the reassurance!
So that fix you suggested produces a bunch of LazySeq
s
And I haven't gotten as far as those yet, in my own learning
The other thing to be aware of in this case is that you're using recursion so you may run into a stack overflow if enough calls are made. You could use recur
instead of match-remove
in that tail call to avoid that.
Okay! Will change immediately
Can you please describe what your function is supposed to do, and maybe provide a couple of example calls to your fn and what you expect it to return
So that people can help you better
Sure! My function is supposed to take in two vectors of strings. Both produced by other functions operating on text files. The function in question is meant to find a word from one vec (i-words)
and remove it from another (t-words)
What if i-words
and t-words
have multiple words in common? Are we supposed to remove all of them?
Also, if one word from i-words
appears multiple times from t-words
, are we supposed to remove all occurrences?
Yes and yes
The goal is to remove every occurrence of each word in i-words
from t-words
Ah, in that case, all we need to do is
(defn match-remove [t-words i-words]
(remove (set i-words) t-words))
No kidding
That's, uh, pretty nifty
Checkout the docstring of remove
by using (doc remove)
in your REPL.
Will do!
What's happening there is that, remove
expects the first argument to be a predicate, a fn that returns true or false when applied to a value. It then applies this predicate on each item in the second argument returning removing the ones for which the predicate returned true.
We're combining that and the fact that sets act as functions that return an item if it exists in the set and nil otherwise, to use the set directly as the predicate.
Meaning we can do stuff like (remove #{"a" "b"} ["a" "a" "b" "c" "b" "d"])
and get back ("c" "d")
It's fairly common in Clojure to find that by thinking about collection-level operations, you can avoid loops and recursion altogether 🙂
In fact, because this sort of thing is so concise, and doesn't represent any abstraction, we tend to use (remove (set i-words) t-words)
inline instead of defining match-remove
(but that can be quite a big step to take, depending on your background, and it doesn't hurt to learn the nuts and bolts of low-level stuff)
@mbbailey96 I only get to Cloj in my rare spare time, and almost every time i do, I hear Yoda in my head.... "you must unlearn what you have learned..." And those "That's, uh pretty nifty" moments keep on happening.
I cannot wait to see how beautiful this lang really is
Oh my lord, I can only imagine!
Even tho' I started with Java back in '97, I was lucky to have learned several FP languages back at university in the early 80's so Clojure's approach wasn't quite so foreign to me... But after nine years of Clojure (eight in production), going back to non-FP languages is really hard for me now!
I only had 4 weeks of exposure to CLisp in a "survey of programming languages" college course. I thought it was brutally logical and mind-bending, but it didn't resonate with me. C was all the rage. I was amazed by C, though my only other language experience was Commodore 64 Basic, and Pascal. 😆
Sounds a bit like my journey. I came out of college and got a job doing C, then C++, having done a fair bit of BASIC and Pascal at college and at home 🙂
Doing Java starting in 96, and in about 5 years after enduring the horrors of JSP and Struts, I started to believe there's got to be a better way. My (big) company aquired this small company that had some kick-a$$ stuff implemented in Erlang, and I was like hello what's this nonsense? But it didn't take much investigation to have the epiphany of OMG I'm not alone, there really is a better way than Java OOP!!
I tinkered with Erlang in my spare time, gained love of FP's ideals, and a few years later I started reading about this Clojure language. ❤️
Let's say I have some function, let's call it consume-service
which consumes some external service. Assuming that the application mode is stored in some global state, what is the Right Way™ to mock out that function based on current application mode? I could convert consume-service
to a multi-method, but that seems overly invasive into the API. Currently I'm simply doing
(defn consume-service [& args]
(if (dev?)
(apply mock-consume args)
(original-implementation-goes-here)))
I think it is pretty clear that the best(simplest?) way to do deal with switching out implementations for tests is if the thing you want to swap out is a parameter (a function argument) instead of a global (a def or defn)
Usually if I am interacting with an external system I like to think of build a kind of proxy object that represents that external system. Then anything that needs to communicate with the external system will get passed that object, and in tests can be passed a different object if needed. There are a few ways to constuct such an object, a pretty natural one is a deftype or defrecord, with supported operations defined as a protocol
there are ways to deal with it if you insist on globals (created with defn or def), but they rely on mutating the global for the duration of the test and mutating it back afterwards, which can behave badly if you ever want to do anything with multiple threads
Thanks for that extensive answer, @hiredman! That seems like a great solution for unit testing, but the aim of this is actually for running the app locally, without external service deps (which requires setup and tokens and config and ...). So I'm also trying to keep the impact on the existing ("original") API to a minimum. The service in question is really just a GET
request to an external API, and that's the only operation required. Do you think that a protocol is still appropriate?
you could just make the proxy (service?) object the function that does whatever you need and pass it around
the main thing is to reify the ability to interact with whatever as some value that gets passed around, so you can pass around a different value if you want