Fork me on GitHub
#clojure
<
2022-02-21
>
Akshay C. Gollapalli02:02:13

Is there a facility for anonymous macros?

hiredman02:02:29

In order to expand macros the compiler needs to know at analysis time which symbols name macros, so any kind of higher order kinds of usage, which I dunno what else an anonymous macro would be for, doesn't work

šŸ‘ 1
Akshay C. Gollapalli02:02:15

Where does your name come from, btw?

šŸ‘€ 1
hiredman03:02:18

In Count Zero (a book) a character refers to another as a "hired man" (maybe just once?)

šŸ“– 1
mico10:02:30

@michael127 have you tried to upgrade amazonica to use the 1.12.* version of aws-java-sdk ? I've found that to use some new features of Timestream I had to upgrade it, and at least for the Timestream it works but of course I am a bit hesitant to make the PR since probably something will break up because there are so many services that Amazonica supports

simongray11:02:44

I once saw some codeā€”I think it was by a guy who used to work for Cognitectā€”that handled memoization in this way: rather than defā€™ing a new var like (def new-f (memo f)), the existing var was redefā€™ed as a memoized version of itself. I canā€™t remember exactly how exactly it was done, so Iā€™m asking here how it might have been handled? It was a simple one-liner.

āœ… 1
Ben Sless11:02:54

(alter-var-root! #'v memoize)

šŸ™ 2
āœ… 1
Ben Sless11:02:42

sans potential syntax errors

tatut11:02:10

or just (def f (memoize f))

tatut11:02:40

as a side note, I have a personal rule to always write a comment describing why this particular memoized case will not have unbounded memory growth... just to be careful with it

āœ… 2
ā˜ļø 1
simongray11:02:32

@UK0810AQ2 Thanks. @U11SJ6Q0K The problem with doing def and returning a function is that it breaks statical analysis, e.g. in Cursive.

simongray11:02:15

@U11SJ6Q0K And I do have a note, although that note is about using core.memoize instead, precisely to limit memory use. It is not relevant for now šŸ™‚

Ben Sless12:02:26

Make that function a component, problem solved šŸ™‚

quoll15:02:19

Note that you can also memoize a function while defining it

quoll15:02:02

Which is useful for recursion. The classic case being fibonacci:

(def fib (memoize (fn [n] (if (< n 2) 1 (+ (fib (dec n)) (fib (- n 2)))))))

quoll15:02:26

Alsoā€¦ I almost never use memoize in production code due to memory use. Iā€™m all about core.cache

šŸ‘ 1
quoll15:02:13

I just referred to this because ā€œone-linerā€ was mentioned šŸ™‚

simongray18:02:00

@U051N6TTC Also breaks static analysis. The alter-var-root trick is the best if you want Cursive to know that itā€™s dealing with a function. Just a shame it doesnā€™t work in CLJS.

quoll19:02:12

Sorry, I have no idea what static analysis does here, nor what it provides, as Iā€™ve never used Cursive. I was responding to the one-liner part

pinkfrog13:02:09

Have you successfully show line number with logback? With this setup

<pattern>%date{ISO8601} [%thread] %-5level %logger{36} line:%line - %msg %n</pattern>
I see inaccurate line number showing up

pinkfrog13:02:27

Everything is in line 8,

Ferdinand Beyer14:02:30

When you also add %file, you will likely see that all messages seem to origin from the same clojure file (depending on how you log to Logback). I think Clojure does not support exposing line numbers to the logging framework.

Ferdinand Beyer14:02:52

How are you using Logback? Through clojure.tools.logging?

Ferdinand Beyer15:02:16

Then you will not receive meaningful filenames or line numbers in your log.

pinkfrog12:02:53

@U031CHTGX1T How do you bypass this? With (:line (meta &form)) in a macro?

Ferdinand Beyer12:02:19

Not sure how this is transported, sorry

jaide15:02:15

How do you handle conditional vectors & hash-maps?

(->> [:a
      (when true :b)]
      (filter identity))
(->> [(when true {:b 2})]
     (into {:a 1}))
Any better ways come to mind?

p-himik15:02:13

filterv, into with a filter transducer, cond-> with assoc or conj.

Arnaud Geiser15:02:35

(cond-> [:a] true (conj :b))
(cond-> {:a 1} true (assoc :b 2))

delaguardo15:02:51

what does "conditional vectors & hash-maps" mean?

jaide15:02:25

When trying to conditionally insert data into them

Arnaud Geiser15:02:27

Note that, unlike cond branching, cond-> threading does
not short circuit after the first true test expression.
https://clojuredocs.org/clojure.core/cond-%3E

delaguardo15:02:07

then, as already mentioned here, there is a spectrum of conditional functions in clojure. like cond->

jkxyz15:02:31

I've had a colleague argue for using splice-unquoting:

`[1 2 ~@(when foo [3])]
I wasn't a fan although it's arguably more declarative šŸ™‚

potetm15:02:45

concat and merge

potetm15:02:04

(except concat doesnā€™t return a vector)

potetm15:02:29

(concat [:a] (when true [:b]))

potetm15:02:41

(merge {:a 1} (when true {:b 2}))

āž• 1
delaguardo15:02:54

(defn bar [foo]
  `[1 2 ~@(when foo [3])])

(defn bar' [foo]
  (cond-> [1 2]
    foo (conj 3)))

(time
 (dotimes [_ 10000]
   (bar true)))
;; => 23.4ms

(time
 (dotimes [_ 10000]
   (bar' true)))
;; => 1.5ms

jaide16:02:05

I think the merge\concat approach is my favorite so far, I like the way it makes the intention really clear. Also like the splice approach, that's pretty concise!

awesome 1
jaide16:02:02

cond-> works too, didn't consider that one but feels like too much given each pair would have a consistent operation so specifying it each time here is not ideal to me

p-himik16:02:38

You can easily write your own macro around cond-> , something like cond->conj or cond->assoc - it would be the most terse and, given a proper name, the most clear.

jaide16:02:50

(concat []
           (if meeting-url
             [(b/text {:href meeting-url} "ā˜Ž")]
             [(b/text {} ":no_mobile_phones:")])
           [(b/text "\t")
            (b/text
             (str (date/time time-zone start)
                  " - "
                  (date/time time-zone end)
                  " "
                  summary))])
Ended up switching from a when to an if so it seems like concat is well suited for this. The cond approach would require a separate meeting-url and (not meeting-url) clauses

p-himik16:02:00

But in this case, your whole block could be replaced with:

[(if meeting-url
   (b/text {:href meeting-url} "ā˜Ž")
   (b/text {} ":no_mobile_phones:"))
 (b/text "\t")
 (b/text
   (str (date/time time-zone start)
        " - "
        (date/time time-zone end)
        " "
        summary))]

jaide16:02:26

That's true, but still trying out the content formatting

p-himik16:02:00

I'm not talking about formatting here but rather about the lack of need for concat and that [], along with [] within if. You can make do with a single vector in this particular case.

jaide16:02:19

Right, I understand

jaide16:02:46

I'm just saying I'm not 100% on what I want this program to do so the flexibility is useful for now, if it doesn't change will probably optimize it

šŸ‘ 1
potetm18:02:45

the real lesson of that article is: donā€™t mix concat with recursion

potetm18:02:11

He alludes to it later: ā€œdonā€™t mix lazy and eager evaluation.ā€

potetm18:02:19

recursion is eager

vemv19:02:30

I also take it as "distinguish between seq and coll fns" often people will use a seq fn whereas a coll one performs better and has no big difference

šŸ’Æ 1
potetm19:02:21

Yeah unfort thereā€™s no eager version of concat. (varargs, nil punning)

potetm19:02:59

(vec (concat ā€¦)) is sufficient tho

p-himik19:02:25

(defn concatv [& args]
  (reduce into [] args))
Short enough. :)

potetm19:02:48

yeah, put it in core, and Iā€™m down to use it šŸ˜„

potetm19:02:19

til then, concat w/o recursion is the biz

potetm19:02:35

(Honestly, I use merge nil-punning way more than concat anyways. Itā€™s fairly uncommon to have conditional concating.)

Linus Ericsson14:02:51

(conj {} [:a 1]) ;;=> {:a 1} could help

Noah Bogart19:02:37

is there a json library that will pretty-print maps with sorted keys?

p-himik19:02:27

A slightly different question, but the answers might be relevant: https://clojurians.slack.com/archives/C03S1KBA2/p1644497018889889

šŸ‘ 1
p-himik19:02:10

Alternatively, you can just walk the resulting data structure in any way you want and replace all map with sorted ones, and then pprint it - all via built-in means.

Noah Bogart19:02:04

hm yeah, i'll have to check that out

Noah Bogart19:02:19

walk is a little more annoying than i'd hoped would exist, but i'll figure something out

p-himik19:02:04

walk will be much less annoying than anything else that doesn't provide a one-line argument like :map-factory sorted-map. :)

Noah Bogart19:02:22

hah right, which is what i was hoping for

p-himik19:02:36

(require '[clojure.walk :as walk])

(defn deep-order-maps [v]
  (walk/postwalk (fn [x]
                   (cond->> x
                     (map? x) (into (sorted-map))))
                 v))

šŸŽ‰ 1
Noah Bogart19:02:04

found the solution for my purposes: (into (sorted-map) existing-map) will decode into a sorted json object using cheshire/generate-string

Noah Bogart19:02:34

oh excellent, that's even better cuz it's recursive

jaide19:02:10

Started off doing a bit of math in an interactive repl I can embed into a doc, turned into "let's overengineer this for fun!" https://replit.com/@eccentric-j/Convertor#main.clj. Working on v2 now to support remainders for more sophisticated calculations

jaide20:02:51

Officially over-engineered https://replit.com/@eccentric-j/conversion-v2#main.clj now but it was fun and learned a bit more about the differenced between symbols and vars. If anyone also finds it kind of fun to solve these trivial problems I'd be interested to see what people come up with!

apt21:02:55

Hi folks. Does anyone know any lib or function that ā€˜flattensā€™ maps in this way? from

{:foo {:bar {:baz 1}}
 :xum 5}
to something like
[:foo :bar :baz 1]
[:xum 5]

hiredman21:02:28

into paths? it is pretty straightforward using for

hiredman21:02:55

(fn f [p m] (for [[k v] m i (if (map? v) (f (conj p k) v) [(conj p v)]] i))

apt21:02:47

Humm. Why does it have two args? One should suffice. Just to clarify, itā€™s supposed to work like this:

(f {:a {:b 2}})
;; => [[:a :b 2]]e

p-himik21:02:34

The first arg is for the recursive call. You can add another arity that would call the 2-arity with [] as the first argument.

hiredman21:02:54

it builds the path as it descends

p-himik21:02:36

There's also a bug in there where it loses k in the false branch. Here's a fixed and formatted version:

(defn f
  ([m]
   (f [] m))
  ([p m]
   (for [[k v] m
         i (if (map? v)
             (f (conj p k) v)
             [(conj p k v)])]
     i)))

hiredman21:02:09

the other option is to build the path on the way up, but building on the way down means all the paths start from the same immutable vector, which means they can share structure

apt21:02:05

Ohhh, okay. Sorry, folks :man-facepalming: Thanks a lot, amazing šŸ’Æ

šŸ‘ 1