This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-04-12
Channels
- # announcements (1)
- # babashka (124)
- # beginners (98)
- # calva (54)
- # cider (32)
- # cljdoc (5)
- # cljs-dev (131)
- # cljsrn (1)
- # clojure (107)
- # clojure-australia (2)
- # clojure-europe (2)
- # clojure-losangeles (1)
- # clojure-norway (3)
- # clojure-uk (28)
- # clojurescript (21)
- # conjure (86)
- # core-async (7)
- # cursive (3)
- # datascript (5)
- # datomic (28)
- # defnpodcast (2)
- # devcards (1)
- # exercism (47)
- # fulcro (22)
- # graalvm (29)
- # graphql (1)
- # malli (5)
- # nrepl (31)
- # off-topic (111)
- # re-frame (23)
- # reitit (4)
- # spacemacs (6)
- # tools-deps (10)
- # tree-sitter (1)
- # xtdb (6)
I'm having trouble with *
throwing integer overflow exceptions when the numbers get too large; how do I fix this by forcing it to auto-promote numbers (I think that's the right terminology).
You can use *'
instead of *
if you want.
or make sure that at least one of the arguments is a BigInteger
OK, that works. Thanks.
In other programming languages python/scala/common-lisp/scheme I often name this function loop or recur. those names are reserved in clojure 😞
I thought about naming it the misspelled recure
@jimka.issy loop
and recur
are functions in Clojure language, so you can just use them, eg. https://clojuredocs.org/clojure.core/loop
no, you can only use loop/recur for tail recursion, not for normal recursion. right?
or am I mistaken?
If you want to write a recursive function, then it simply calls itself until some termination condition is reached (eg. a boolean condition). You dont have to use loop and recur.
I just tried doing defn on a function named recur in Clojure. The defn gave no error, but you cannot call it with an expression like (recur 5)
, since the Clojure compiler treats that like the built-in recur
indeed. my question is for whether there's some idiomatic name for such a local function. I generally use a local function because that function usually takes a different argument list than the parent function. i.e., and extra accumulator argument, or omits other arguments which are invariant.
you can name them -loop
, -recur
, iter
, etc.
any variation on iter
would be more idiomatic because it won't overload the meaning of loop/recur (even if you decorate the name, the meaning might still be ambiguous)
There's also a fairly common idiom where you have a foo*
fn that does the main work of the foo
fn, i.e. foo
just sets up the arguments to pass to foo*
which handles the recursion or whatever.
I name custom function definitions (`defn` ) based on its purpose, not based on the type of algorithm it uses. What you are describing sounds like how I use loop and recur (should I need to go to that low level of abstraction). A very simple example is here https://github.com/practicalli/four-clojure/blob/master/src/four_clojure/022_count_a_sequence.clj#L40
A more complex example, which as you see further on in the code can be greatly abstracted using clojure.core
function https://github.com/practicalli/four-clojure/blob/master/src/four_clojure/053_longest_increasing_sub_seq.clj#L48
Not sure if we are missing each other. I use loop/recur when it is appropriate to do so. i.e., when i need a locally tail-recursive function which is called automatically. however, when I need to do something like loop/recur, but the function is not tail-recursive, I have to define a local function using letfn
and then give the function a name. I can't use loop, nor recur for that function name.
i usually just name it f
or tbnl
to-be-named-later
loop/recur is an excellent set of names for this common pattern. The clojure authors did will in choosing that name.
the names are clear and evocative.
Just name it loop_
. f
is also a common name. I tend to name such one off recursive functions step
.
If I cant think of a meaningful name for a function suggests I havent thought about the problem enough. Using vauge names in your own custom code leads to technical debt... Its also a sign that I should try and abstract the code a bit more or refactor out sections into their own function. It may be useful if you share a code example
@jr0cket I beg to differ. For local one off things, I think it is okay to not spend too much time on naming. Otherwise, anonymous functions wouldn't have existed. Except, with anonymous functions, you cannot really recur, so, you name it something.
Just to be clear, I agree with what you said for def/defn
.
I respectfully disagree, all code is important in communicating intent. If you are going to name something, then you should name it on is purpose, why is it in existence. If its not known why its doing what its doing, then why is code being written to do it? If its a generic algorithm, then avoid naming it at all (unless its complexe, then its probably not that generic and some more hammock time is probably in order)
I mean, in the context of just enabling recursion. Like how loop/recur does not have a name. A for, a doseq do not have names. I mean the same way a function named for the sole purpose of recursion can be named step or f or whatever.
I am confused. The functions loop, recur, for, doseq all have names, otherwise you could not call them. They are also well established idioms. If custom code is written to do recursion then the assumption is that is does something different to those functions. If so, why is that the case. Is it an oversight in the design or is it a specific intention that has a purpose. If its a specific intent, then that is something additional that needs to be learnt about this code that should be understood before working with it. Adding a meaningful name, comment and documentation would be appropriate to make working with the code simpler.
for
has a name, but what that for
is doing in a specific context does not have a name.
Sorry, very confused about that statement too.
for
is the name the function defined in clojure.core
which is documented and has many examples of its use available (not all good, but there are some valuable examples too).
Names are used with for
to add context, otherwise what do you put in the []
? The same goes for loop
, what you put in the []
provides context as to why you are recuring? Only putting single character names that could mean anything make that custom code more complex.
There seems no reason to hide any knowledge about why you are recuring when you can express it with meaningful names. If names are not obvious, then its a warning sign that you either need some hammock time or need to go and ask some questions.
for starters, the body of for
is an anonymous inline function is what they're saying and
perhaps naming it recurring
isn't so bad
what is it recurring?
I guess it is recurring what its arguments are.
(defn rte-trace
"Given a compiled rte, find a sequence of types which satisfy the corresponding pattern."
[rte]
(letfn [(recurring [state path lineage]
(cond
(:accepting (rte state)) path
(some #{state} lineage) false
:else (some (fn [[type dst-state]]
(recurring dst-state (conj path type) (conj lineage state)))
(:transitions (rte state))))
)]
(recurring 0 [] ())))
as an example.One other area where I am often unable to name things is transducers. How do you even name a combination of maps and filters?
(I know the convo is starting to move on from this but) the idea that every function needs a name is to say there should never be a truly anonymous function; that we can create functions on the fly, but that they should be tied to an identifier somewhere to state intention (and, in its absence, surely a comment). I'd think hindol was using for
and loop
as a counter example where we all flat out use truly anonymous functions all the time; the body of,say, a for
is an anonymous function, defined inline (by virtue of for
being a macro, which for
will then use to do its particular flavor of looping).
Then, they were saying in cases where its ok to have a truly anonymous function, but where you still technically need a name not for identifying, but for the looping itself, then its ok to name it step
or something like that since in that case, the name only serves for the looping itself, not for stating an intention (by definition).
I personally agree with the idea that in plenty of cases, its ok for a function to truly be anonymous. When they should and shouldn't be is another conversation of course -- I just believe there are many cases where its ok. And note, you guys might agree more often than not as to when that is -- because in many cases where something is 'anonymous', it might be not what someone is thinking of anonymous, because its really a case where its overall intention / naming is inherited from a close by, 'surrounding' name -- such as the example
(defn grades [school-db]
(for [student (get school-db :students)]
(get student :grade)))
We didn't name our looping-step in for
, but we can sort of bubble up to the overall function definition (`grades`) to know that the body of the for
is surely, basically, grade
.
One might also argue that in a case where you're naming a looping step for recursion, you might as well name it something good since you're already naming it. I personally won't; I like step in many of those aforementioned cases where we've agreed we know the intention, but I could at least see the argumentThank you for explaining it so clearly. I was not finding the right words. Indeed my argument was name when it makes sense, and sometimes it just doesn't.
Of course, another example off the top of my head I suppose; this is an example where the looping step is right there with the looping construct. I can think of an example as well where the looping step is for some reason is much further from the loop, or there are many different looping steps -- without an example, its hard to say whether this would practically happen a lot, or whether or not it would already inherently be a code smell, but regardless in a case like that I could see a better name becoming relevant. But I suppose again our point was not that you always would name a looping step
or the like, but that you wouldn't always not do so
Hi, I could search for this somehow but it's really hard. I understand the keyword and the lambda but I don't understand the ..
and the %
I know that -target - value is to get ev.target.value and I think %
the argument for the lambda, but what is ..
?
12 :onChange #(set-asset-type (.. % -target -value))
There is some documentation on ..
on this page documenting Java interop: https://clojure.org/reference/java_interop
Any expression with #( )
surrounding it in Clojure is a shorthand for an anonymous function. Inside of there, occurrences of %
or %1
refer to the first argument of this anonymous function, %2
to the second argument, etc.
For example, #(inc %)
is equivalent in the anonymous function created to (fn [x] (inc x))
The #(inc %)
syntax is just a bit shorter. #(inc %)
is not a great example, because it is equivalent to inc
Slightly better example is #(+ % 5)
, which is equivalent to (fn [x] (+ x 5))
There is also the doto macro, which does function chaining common in OOP languages. It injects the same object into the subsequent step, but possibly mutated.
@U0CMVHBL2 I think you missed a # in the last example.
sorry, yes, I did. Will try editing it after your message, in case anyone reads your pointing that out after I make it correct.
I am reading the docs. I probably read it before, but unless I use the knowledge for something, it's like I never read it. : (
Thread first means the result of the previous step becomes the first argument of the function that makes the next step.
I am on phone, so forgive any typo but here is an example,
(-> x f (g y))
Here, x is injected as the first argument to f, and the result of that is injected as the first argument to g.
Equivalent to, (g (f x) y)
.
Does that make sense?
So indeed %
is the argument for the lambda. As for what ..
is, its easier to show why it exists
First, for my example I'm going to rename %
to ev
, because that's what it really is in this particular case.
If you want ev.target
, then in Clojurescript that's going to be
(.-target ev)
(you wouldn't write (.target ev)
because that would translate to a ev.target()
in javascript)
And if you wanted ev.target.value
, first you're going to need to get ev.target
, and once you have that call .value
on the result of that, so
(.- value (.- target ev)
..
is just a nice shorthand for this sort of nested javascript interop, that also looks similar to what you might be expecting in a vanilla language
(.. ev -target -value)
(which is sort of like saying 'get -target from ev, and then get value from that result'). You'll notice it also lets you drop the .
from target and value, since it can infer that by knowing everything inside of it is interop, and is either .-field
or (.method-call blah)
If you wanted date.month.first_letter()
you might have
(.. date -month (first-letter))
;; or
(.. date -month first-letter)
I wish ..
would have been added to the CLJS cheat sheet. Here I’ve been putzing around with oget
and oset!
.
This is so much nicer (-> js/document (.getElementById "root") .-innerHTML (set! "yay"))
..
is still convenient as we can avoid the extra dots while doing interop. I thought that was the purpose.
https://gist.github.com/Lokeh/e93a1a0ab25d40df006d77f405c1e535#file-helix_example-cljs-L22 This is where I saw it
There's no real purpose of one over the other. ->
is more general, works in more situations, and happens to cover the use case that ..
also handles. Which is why I and some others prefer to use it over ..
mostly because you can be consistent since it can apply to interop as well as non interop code.
yeah I often just end up with -> , I don't mind an extra .
, and I get to use whatever with it
But ..
is still there, and if you enjoy not needing to put a .
feel free to use it if you prefer it's ergonomics
Oh, well I mean in Clojure, on ClojureScript they chose not to have ..
in favour of ->
which makes sense I guess, but that's also just a choice that the ClojureScript authors made
Why doesn’t contains?
support lists?
I've looked up in the docs, it says that contains?
checks if a key is present.
For the list, keys are numeric indices, so you can check for their presence (but probably (< idx (count lst))
is better)
Thank you. Yeah, I should have checked clojuredocs before asking.
I don’t think contains?
works on lists, although it does work on vectors. I believe the reason that contains?
doesn’t work on all sequences is that contains?
wants to be able to promise constant time and that’s not possible with lists
@U7RJTCH6J just checked in repl — it actually works, but checks for index keys:
(contains? [:a :b :c] :a)
=> false
(contains? [:a :b :c] 0)
=> true
(contains? [:a :b :c] 3)
=> false
my bad
no worries
lists/vectors/arrays are often used interchangeably in conversation, but it has a subtle distinction here ¯\(ツ)/¯
is there another function which I can use to look for an element in a sequence or list ?
ok, I found some
which works in my use case.
yea, I think contains always trips everyone up because you're expecting it to be a function checking that a collection ..er, contains a value, but its checking that it contains a key. And for that reason, it doesn't work on lists because they are not considered keyed sequences
I fell in the trap after 2 years of Clojure 🙂
I thought one of the main reasons contains?
doesn’t work on lists is to guarantee:
> ‘contains?’ operates in constant or logarithmic time;
it also sort of explains the behavior with vectors and maps
and also explains why contains?
doesn’t support some other sequence types
@vincent.cantin some
checks for logical truthiness and is a little quirky. For an arbitrary element, (some #{<element>} coll)
works... except nil
and false
. You need (some false? [1 false 3])
and (some nil? [1 nil 3])
.
I solved my problem, thank you.
Well, I'm pulling the 'not considered keyed sequences' from one pair of docs (I almost added an additional message of 'I don't know what that itself inevitably means; lists, for instance, are clearly ordered, and can clearly then be mentally assigned indexes, and from that keys. I don't know if it has to do with their lower level implementation as (cons x (cons y ..))
[or rather more specifically, head = value
, tail = another sequence
if I remember] over something that itself is indexed' but then I stopped there, as I don't want to say anything inaccurate about their implementation without looking into it again further as I'm a bit fuzzy here) so if there's something written implying something else, I would not be able to argue one as being more true over the other; we'd be at a document he-said-she-said, unless one pair of docs has some tie breaker. I think I got the 'lists are not considered keyed sequences' from clojuredocs, if that helps
it has to with indexed lookup and performance
keyed/indexed lookup == better than linear time
@zdot101 If you're interested contains?
is implemented as clojure.lang.RT/contains
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L830-L854
A question to long time clojurians what kinds of structural editing (or editing tips in general) are you using?
emacs' lispy 👀 hands down, although we were just talking on #emacs on how it has several sharp edges
in emacs i use a few built-ins, the 3 at the top of https://www.gnu.org/software/emacs/manual/html_node/emacs/Expressions.html
@sfyire I use both Parinfer and Paredit (with Atom), but the most common stuff I use is slurp and barf to grow and shrink expressions (by moving parens around).
Not everyone likes Parinfer -- it does take a bit of getting used to but I really like it for simple code editing: it moves parens around as you indent/undent code so that the structure follows the layout.
The replies are a lot more varied than I thought :o anyone else got a different approach?