Fork me on GitHub
#clojure
<
2022-03-30
>
ivana05:03:00

Hello! Is there any stdlib fn or macro, which works like (defn t [pred val default] (if (pred val) val default)) ?

Karol Wójcik06:03:11

This is basically if.

☝️ 1
ivana06:03:41

exept of its first argument, whih is need to be a boolean value instead of predicate itself

craftybones06:03:14

no core functions or macros. Small enough to write your own one liner

ivana06:03:55

Thaths why I'm asking - it's not a problem to write custom fn or macro, but I try to find existed ones first

craftybones06:03:13

Can’t think of any.

pavlosmelissinos06:03:30

`fnil` comes close but doesn't work exactly like that (edit: nevermind, you want to replace the output)

ivana06:03:34

fnil is good for checks on nil? but not on custom predicate. I just thought, that there shoul be a common pattern for such type of code in dynamic types world...

craftybones07:03:40

There are other ways to do type checking if that’s what you want.

ivana07:03:38

Can you share them, please?

craftybones07:03:57

clojure.spec for one

craftybones07:03:05

And other spec related tools

craftybones07:03:17

such as malli or schema

craftybones07:03:58

What exactly are you trying to do, if I may ask?

ivana07:03:24

I'm trying to find an usable way for checking & setting defaults to values, which are not of proposed types or nils

craftybones07:03:40

For a specific instance or as a pattern in general?

ivana07:03:32

What woul be more short & readable

ivana07:03:40

f.e. I can coerce val to string as (str val) and it makes nil as "" and 3 as "3" but it will keep [1 2 3] as "[1 2 3]" what can be undesireable

craftybones07:03:03

Can’t you simply have a map of undesirables mapping to their default values?

craftybones07:03:24

(def defaults {nil 42})

(defn make-default [x] (get defaults x x))

(make-default 42) ;; returns 42
(make-default nil) ;; returns 42

ivana07:03:53

Let talk about this case:

{:a nil} => 0
{} => 0
{:a 3} => 3
{:a "3"} => 3
{:a ....} => 0

ivana07:03:10

It's a more complex one, I know, we should parse a string to int, but it's just an example of one case when I want to use my check helper

ivana07:03:31

(t (some-fn number? string?) val 0) like this

ivana07:03:17

And yes, it may looks like "inline spec" from this point...

pavlosmelissinos07:03:19

You can use custom predicates but that will quickly become unmanageable because you're not simply "removing undesirable values" nor parsing. You're describing a conversion process that (potentially) involves a significant amount of logic. Like I said in the other thread I really believe that you should use a library like https://github.com/noprompt/meander to unnest your data. For the coercions you can use something like https://github.com/exoscale/coax.

ivana07:03:32

Thanks, I'l look on them and maybe really will try it!

ivana06:03:19

And another one, which works like (defn v [val default] (if (nil? val) default val)) ? or fails on booleans

craftybones06:03:37

This is basically if-not 😄

ivana06:03:07

exept of its first argument, whih is need to be a boolean value instead of predicate itself

ivana06:03:31

and 2 args instead of 3

craftybones06:03:55

Right. Might as well wrap it into your own fn. Can’t think of anything in core that does this

craftybones06:03:08

You can hack it

ivana06:03:09

((fnil identity default) value) f.e. looks like one of the ways?

pavlosmelissinos06:03:38

Is that better/more readable than (if (nil? val) default val)?

pavlosmelissinos06:03:39

Are you looking for a way to not have to repeat val or is there more to it?

ivana06:03:59

Similar +-. The winner is (or val default) but it substitutes false (((

ivana06:03:52

I'm looking for usable way to set defaults instead of nils in code

ivana06:03:04

F.e. (:active {:id 1 :active nil} 42) => nil so I need a short wrapper to avoid nils

ivana06:03:25

And yes, val can be a big form, so I'd like to type it once

pavlosmelissinos06:03:28

1. I'd go one step back: Do you control what goes into the map? If so, try not to have nils before you build the map and then you'll be able to just (merge default-map m). No or/ifs anywhere. 2. If not, I'd just write (or (:active {:id 1 :active nil}) 42) - oh, or will replace false too though, right that's what you said 3. if you're absolutely sure that you don't need any of the nil vals you could remove those keys (this is risky though) and then do a merge with a default map

ivana06:03:13

This is on of the cases, but I have to write stable backend code having input map from json, and frontend can either send nil values or does not send them as well. I can sanitize and delete all the nils (on all the nested levels!) before, but looks like it is overkill. I'm looking for simple & usable solution, standart one first

👍 1
pavlosmelissinos06:03:13

Right, I don't think there's much you can do in that case. You could write your own wrapper function and use it everywhere to protect your data from unwanted nils but I've actually been there and it's not pretty. However: > nested levels Have you considered transforming your data with something like https://github.com/noprompt/meander of https://github.com/healthsamurai/ironhide? As your project grows I suspect you'll have bigger problems if you have nils and nested data to handle.

ivana06:03:47

I just entered new team & project, and don't know about input data transformation process yet, but I already see the hacks & tricks in the code and my first idea was to cleanup it as possible

👍 1
pinkfrog12:03:14

For 1.11, the passing map to keyword argument function (https://clojure.org/news/2021/03/18/apis-serving-people-and-programs) what actual pain point does it solve?

Alex Miller (Clojure team)12:03:01

the main problem is that callers must list trailing args (good for people) but calling functions (programs) often have stuff in a map. this lets those functions pass the data directly without needing to apply/spread the kwargs

thanks3 1
Alex Miller (Clojure team)12:03:25

you commonly run into this when trying to layer one kwarg function over another

Joshua Suskalo15:03:36

yeah, if you've ever worked with a kwarg-only API and needed to programmatically choose which elements to add, you'll probably have made your own custom apply that first flattens an input map into a sequence before passing it to the function. Now that wrapper function need not exist except in codebases looking to support multiple clojure versions.

borkdude12:03:13

@i For one thing, you can pass a regular map to a named argument function instead of having to do stuff with apply

thanks3 1
🧵 1
orestis18:03:47

https://twitter.com/FakeUnicode/status/1490262540030713856?s=20&amp;t=5KnaGhc0j-FelgLlDCWHrw I'm so frustrated I can't make a Clojure version of this that fits in a single tweet 😄

orestis18:03:34

(let [s (vec " ▁▂▃▄▅▆▇█")
      walk (fn [n]
             (let [n (+ n (rand-nth [-1 1]))]
               (cond
                 (> 0 n) (+ n 2)
                 (<= (count s) n) (- n 2)
                 :else n)))]
  (->> (iterate walk 0)
       (map #(nth s %))
       (take 5000)
       (clojure.string/join))
here's what I have so far.

orestis18:03:26

(require '[clojure.string :as s])
(let [s " ▁▂▃▄▅▆▇█"
      w (fn [n]
          (let [n (+ n (rand-nth [-1 1]))]
            (cond
              (> 0 n) (+ n 2)
              (<= (count s) n) (- n 2)
              :else n)))]
  (->> (iterate w 0)
       (map #(nth s %))
       (take 5000)
       (s/join))
A bit smaller, don't need to use vec

p-himik18:03:54

You can make w a #(...) function. Not sure if you've tried removing all the unnecessary whitespace. :)

Cora (she/her)18:03:20

maybe apply str instead of join

Ben Sless18:03:04

(let [s " ▁▂▃▄▅▆▇█"]
  (clojure.string/join
   (take
    5000
    (map
     #(.charAt s %)
     (iterate
      (fn [n]
        (let [n (+ n (rand-nth [-1 1]))]
          (if (> 0 n) (+ n 2) (if (<= (count s) n) (- n 2) n))))
      0)))))

Ben Sless18:03:53

(let [s (vec " ▁▂▃▄▅▆▇█")]
  (clojure.string/join
   (take
    5000
    (map
     #(s %)
     (iterate
      (fn [n]
        (let [n (+ n (rand-nth [-1 1]))]
          (if (> 0 n) (+ n 2) (if (<= (count s) n) (- n 2) n))))
      0)))))
235 chars

Ben Sless18:03:16

(let [s (vec " ▁▂▃▄▅▆▇█")]
  (clojure.string/join
   (take
    5000
    (map
     #(s %)
     (iterate
      #(let [n (+ % (rand-nth [-1 1]))]
        (if (> 0 n) (+ n 2) (if (<= (count s) n) (- n 2) n)))
      0)))))
218

Martin Půda18:03:25

(apply str (take 5000 (map #(nth (vec " ▁▂▃▄▅▆▇█") %) (iterate #(let [n (+ % (rand-nth [-1 1]))] (cond (> 0 n) (+ n 2) (<= 9 n) (- n 2) :else n)) 0))))

Ben Sless18:03:54

(apply str (take 5000 (map #((vec " ▁▂▃▄▅▆▇█") %) (iterate #(let [n (+ % (rand-nth [-1 1]))] (if (> 0 n) (+ n 2) (if (<= 9 n) (- n 2) n))) 0))))
144

Ben Sless18:03:44

:man-golfing:

p-himik18:03:48

Twitter truly brings out the worst in people.

❤️ 3
😂 3
genmeblog19:03:42

(apply str (take 5000 (map #((vec " ▁▂▃▄▅▆▇█") %) (iterate #(let [n (+ % (rand-nth [-1 1]))] (cond (> 0 n) (+ n 2) (<= 9 n) (- n 2) 0 n)) 0))))
143

Ben Sless19:03:45

142!

(apply str (take 5000 (map #((vec " ▁▂▃▄▅▆▇█") %) (iterate #(let [n (+ % (rand-nth [-1 1]))] (+ n (if (> 0 n) 2 (if (<= 9 n) (- 2) 0)))) 0))))

Ben Sless19:03:08

but wait, there's more, we can get rid of whitespace!

p-himik19:03:37

@UK0810AQ2 Replace (- 2) with just -2. :)

Alex Miller (Clojure team)19:03:44

(> 0 n) is neg? isn't it?

Alex Miller (Clojure team)19:03:54

I guess that's bigger, nvm

Ben Sless19:03:14

(apply str(take 5000(map #((vec " ▁▂▃▄▅▆▇█")%)(iterate #(let[n(+ % (rand-nth[-1 1]))](+ n(if(> 0 n)2(if(<= 9 n)(- 2)0))))0))))

Ben Sless19:03:40

124!

(apply str(take 5000(map #((vec " ▁▂▃▄▅▆▇█")%)(iterate #(let[n(+ % (rand-nth[-1 1]))](+ n(if(> 0 n)2(if(<= 9 n)-2 0))))0))))

Ben Sless19:03:07

I think my work here is done :rolling_on_the_floor_laughing:

p-himik19:03:14

You can also replace ((vec ...)%)with (nth ...%) . Removes ().

Ben Sless19:03:32

right, you can take nth from string!

Ben Sless19:03:02

121 chars

(apply str(take 5000(map #(nth" ▁▂▃▄▅▆▇█"%)(iterate #(let[n(+ % (rand-nth[-1 1]))](+ n(if(> 0 n)2(if(<= 9 n)-2 0))))0))))

genmeblog19:03:15

also condition returns either 1 or 7 or n

Ben Sless19:03:20

Alex is probably horrified at this point

😂 1
genmeblog19:03:28

(apply str (take 5000 (map #(nth " ▁▂▃▄▅▆▇█" %) (iterate #(let [n (+ % (rand-nth [-1 1]))] (cond (< n 0) 1 (> n 8) 7 0 n)) 0))))
128 with spaces

p-himik19:03:55

@U1EP3BZ3Q Not equivalent since in your code the new value of n does not depend on the old value of n, except for one branch. In the original solution, all three branches depend on n.

genmeblog19:03:06

I don't agree :)

Ben Sless19:03:11

if n >= 9 it has to be 9, then n-2 is 7, similarly for the other case

Ben Sless19:03:25

this is a non uniform correction for over/under flow

p-himik19:03:58

Ah, right, makes sense. The yuk factor grows incessantly.

😂 2
Ben Sless19:03:22

How is that different from cross product when one direction is zero? 😛

1
genmeblog19:03:09

(apply str (take 5e3 (map #(nth " ▁▂▃▄▅▆▇█" %) (iterate #(let [n (+ % (rand-nth [-1 1]))] (cond (< n 0) 1 (> n 8) 7 0 n)) 0))))
127 with spaces

😆 1
❤️ 1
genmeblog19:03:30

Here is bigger collection: https://www.ioccc.org/

Ben Sless19:03:52

yeah but how many are also quines?

Cora (she/her)19:03:20

we've been reducing the size of the current algorithm but what if we did something different, like using reduce to build up the string

genmeblog19:03:05

Maybe, but here is the "102" chars version:

genmeblog19:03:49

(apply str (take 5e3 (map #(nth " ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁" (mod % 16)) (iterate #(+ % (rand-nth [-1 1])) 0))))

11
🤯 1
genmeblog19:03:09

I knew we could get rid of this condition

Cora (she/her)19:03:35

I was staring at the 8 characters trying to figure out how to make mod 8 not reset to 1

Cora (she/her)19:03:43

and of course the solution is to make it 16

Cora (she/her)19:03:29

if that makes sense

👍 2
craftybones05:03:26

There is another way of removing the if mathematically

(defn foo [s z]
  (let [z (+ z (rand-nth [-1 1])) 
        z (+ z (* -2 (Math/floor (/ z (dec (count s))))))] 
    (int z)))

👍 1
genmeblog05:03:21

Yes, true. Don't forget that the goal is to make it shorter.

craftybones05:03:31

Yes, your solution is significantly more elegant.

craftybones05:03:46

I was just making the point that there is a mathematical solution to it 🙂

craftybones05:03:55

Clamping is always annoying. Of all the ifs that get your goat, clamping is probably one of the worst ones

Ben Sless05:03:29

What I like the most about your last solution is how by modifying the static input you got a more elegant expression

hkjels06:03:56

There’s now even space for clj -e or bb -e '' in that tweet. Impressive!

orestis15:03:17

clj-kondo complains about 5e3 being a double, not a natural integer 🙂

orestis15:03:56

@U1EP3BZ3Q mind if I post a (slightly modified version) of this on Twitter and mention you?

genmeblog15:03:51

@U7PBP4UVA sure!!! don't forget about mentioning the rest of people from this thread, a lot of contribution to the final result

genmeblog15:03:28

clj-kondo doesn't know the real hacking 🙂

leifericf18:03:05

Haha, this thread is ridiculous. And I love it.

Drew Verlee03:04:15

We were so preoccupied with if we could, we didn't stop to consider if we should.

❤️ 3
mbjarland09:06:54

Fantastic thread and go @U1EP3BZ3Q and clojure for getting this down to 102 chars! :punch:Somewhat unintuitively I think code colfing actually tends to teach you things about both the language and the problem domain.

bananadance 1
Rachel Westmacott16:08:57

I think we can squeeze a little more out - a vector is a function, so (vec " ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁") is equivalent to #(nth " ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁" %) and 3 characters shorter. We just need to push the mod onto the iterate thus: (apply str (take 5e3 (map (vec " ▁▂▃▄▅▆▇█▇▆▅▄▃▂▁") (iterate #(mod (+ % (rand-nth [-1 1])) 16) 0)))) comes in at 99 characters.

🎉 1
Joshua Suskalo21:03:01

Hey, when working with the clojure cli, it looks like our polylith project is outputting no deps besides clojure and its dependencies when doing clj -A:dev -X:deps tree, where the :dev alias has all the actual dependencies of the project.

Joshua Suskalo21:03:08

Is this a known behavior and I'm just missing something?

Joshua Suskalo21:03:10

Or is that a bug?

Alex Miller (Clojure team)22:03:38

It’s a misunderstanding of how this works

Alex Miller (Clojure team)22:03:16

You're running the deps tree tool program and adding aliases to that program (which has no effect on the calculated classpath)

Alex Miller (Clojure team)22:03:31

But you can do that too …

Alex Miller (Clojure team)22:03:27

clj -X:deps tree :aliases ‘[:dev]’

❤️ 2
reedho23:03:45

How does this different with clj -Stree works internally?

Alex Miller (Clojure team)23:03:14

-Stree is run in your project classspath so the aliases are in effect

Alex Miller (Clojure team)23:03:13

-Stree may eventually fade in prominence, the :deps tree continues to add more features

🙏 1
Joshua Suskalo14:03:54

Thanks so much!

Alex Miller (Clojure team)22:03:45

You'll need a pretty recent release of clj for that to work