Fork me on GitHub
#beginners
<
2022-06-21
>
sheluchin13:06:30

(update {:x 1} :x (partial hash-map :nested))) 
; => {:x {:nested 1}})                         
Is there a way to do this without the partial?

lsenjov13:06:23

Not.. really? If you really don't want to use the partial you can do it with an anonymous func

(update {:x 1} :x (fn [y] {:nested y}))

sheluchin13:06:13

Anything shorter? I feel like I've seen an example that simplifies it beyond partial/fn.

delaguardo13:06:38

(update {:x 1} :x #(hash-map :nested %))
the same as above but uses anonymous function syntax

sheluchin13:06:23

Is there a style preference in the community?

delaguardo14:06:27

not the community preference though

sheluchin14:06:08

Okay, thanks very much.

lsenjov14:06:08

As a aside to this - clojure is already a terse language. Prefer readability to code golfing

👍 2
1
quoll18:06:21

Depending on the location of the code, if this runs in a hot loop, then I’d use fn/#() over partial, since there is a performance difference

quoll18:06:30

Personally, I like #() over fn for single statements, and fn when there is more than one thing to do and/or I want destructuring. It’s purely a style thing, since they’re read exactly the same way. i.e.

user=> '#(hash-map :nested %)
(fn* [p1__2#] (hash-map :nested p1__2#))

👍 2
plexus05:06:47

For cases like this I'll sometimes use #(do {...}), so (update {:x 1} :x #(do {:nested %})). I find that having the literal map in there scans a bit better.

sheluchin12:06:10

@U07FP7QJ0 thanks, that's a nice alternative, although I always feel kinda guilty when resorting to stuff like that because it doesn't seem like the "proper" way. It definitely reads easier though.

quoll12:06:27

Now I need to inspect the bytecode of these 🙂

kennytilton18:06:43

What does mean in clojure as a parameter placeholder? I typo-ed up up a closure like (fn [_ me new-value __] ...) and it seemed to think I mean four parameters. Just curious. Oh, wait, we have a repl:

((fn [a __ b] (+ a __ b)) 2 3 4)
=> 9
OK, __ is a valid symbol for a param. 🤷

quoll18:06:42

It works for a typo-ed closure like that, because you shouldn’t repeat argument names. That said, you CAN repeat argument names, and this squicks me out 🙂

quoll18:06:22

=> ((fn [a a] a) 5 6)
6

kennytilton19:06:04

Looks like _'s get rebound to last parameter's argumen.

((fn [a _ b _] (+ a _ b _)) 2 3 4 5)
=> 16
And @U051N6TTC is right!
((fn [a a a] (+ a a a)) 2 3 4)
=> 12
I might have to go all the way back to C. 🙂

😁 1
kennytilton19:06:50

Thx, @alexmiller, I meant to reference that page for the benefit of newcomers. Silent, however, on the double-underscore...hang on:

((fn [a _ b] (+ a _ b)) 2 3 4)
=> 9
((fn [a _ b _] (+ a _ b _)) 2 3 4 5)
=> 16
I am going back to Common Lisp. :rolling_on_the_floor_laughing:

andy.fingerhut19:06:43

Underscore is a valid character to use in Clojure symbols, I believe. The leading underscore on a symbol as documented at the link Alex gave is a common convention in Clojure, not enforced by the language, although often used by lint tools

hiredman19:06:31

The underscore basically had nothing to do with this example, it is just illustrating the scoping of repeated argument names

👆 1
quoll19:06:48

I figure it does something similar to let bindings, so it’s sort of the equivalent to:

(let [a 2
      _ 3
      b 4
      _ 5]
  (+ a _ b _))

hiredman19:06:57

So for example just replace _ with x

quoll19:06:31

I said that in the thread prior to this one. Only I used a rather than x

quoll19:06:55

I note that someone saying that they’re going to a language they imply to be better, suddenly got a lot of responses 🙂

😆 1
andy.fingerhut19:06:35

I do not have a REPL handy to try at the moment, but I would bet multiple Clojure lint tools might give a warning about using repeated symbols in arg vectors

quoll19:06:04

I don’t actually use any linting tools, but I would be very disappointed if they didn’t!

delaguardo19:06:01

clj-kondo throws a warning about unused binding 🙂

rolt19:06:35

it's the same behaviour in common lisp though...

dpsutton19:06:11

in sbcl i got an error

* ((lambda (a _ b _) (+ a _ b _)) 2 3 4 5)
; in: (LAMBDA (A _ B _) (+ A _ B _)) 2
;     ((LAMBDA (A _ B _) (+ A _ B _)) 2 3 4 5)
;
; caught ERROR:
;   The variable _ occurs more than once in the lambda list.

dpsutton19:06:44

same error in guile scheme

rolt23:06:06

my bad i only tried the first online CL interpreter i found, so probably incomplete. Elisp does have this behaviour however

Alex Sky08:06:47

never seen a problem with this in long practice with clojure imho If you keep in mind that the code is data, then everything makes sense

kennytilton19:06:34

Oh, wait, I read too fast: "no special meaning". Just a convention. M'kay. That would wreak havoc in CL with ignored variable warnings. But the real howler here is that we can repeat parameter names. Love to know the history of that choice.

andy.fingerhut19:06:48

I would be a little surprised if it was an explicit design choice, but maybe it is. It seems more likely to be a corner case behavior that wasn’t deemed worth doing anything in the language to prevent, after it was pointed out.

andy.fingerhut19:06:03

It is definitely an explicit design choice to allow the same symbol name to be bound multiple times sequentially in a ‘let’ expression’s bindings.

madstap02:06:47

I just now realized that I have done the following more than once and never thought about it.

(add-watch (atom nil)
           :foo (fn [_ _ _ new] ,,,))
It is pretty convenient in this case. Clojure linters respect the convention and don't give ignored variable warnings when the name starts with an underscore.