Fork me on GitHub
#clojure
<
2019-02-03
>
john04:02:36

You know what might be nice? An thread macro that can optionally escape forms, like (-> n dec #esc #(- 9 % 1) inc)

john04:02:00

such that, instead of the value being threaded into the beginning or end of the anonymous function, the fn is allowed to convert itself into its evaluated form, and the thread sees it as a one arg fn.

john04:02:21

What would be an ideal notation for that thread escaping?

seancorfield04:02:48

Why wouldn't you just use as-> for that? (-> n dec (as-> n (- 9 n 1)) inc)

john04:02:42

Cause then I could just use -> for everything, and escape optionally

seancorfield04:02:11

Seems to me that as-> is the "escape" function...

seancorfield04:02:43

(-> n dec (as-> % (- 9 % 1)) inc) if you prefer % for the argument name.

seancorfield04:02:22

(-> n dec #esc #(- 9 % 1) inc) it's not all that different -- and it's built-in, no language extensions needed

john04:02:42

(-> n dec #~(- 9 % 1) inc) that might look prettier... and I wouldn't have to switch between two different thread macro contexts

john05:02:20

or (-> n dec #~#(- 9 % 1) inc)

seancorfield05:02:01

Meh... you've saving so few keystrokes, and introducing non-standard syntax... I see no benefit.

john05:02:08

you could even switch some assoc's cleaner (-> n (- 3) dec #~#(assoc state :n %) keys)

seancorfield05:02:33

(-> n (- 3) dec (->> (assoc state :n)) keys)
(-> n (- 3) dec (as-> % (assoc state :n %)) keys)
(-> n (- 3) dec #~#(assoc state :n %) keys) ; more cryptic and only one character shorter?

john05:02:56

Yeah, I use multiple thread macros some times

john05:02:43

I hear you, I'm not completely sold on it either... I'd have to kick the tires on it some more.

john05:02:37

And I'm not saying add a reader macro for #~ language wide. Just add another thread macro that detects it, like -#> or something

seancorfield05:02:55

Code golf. Doesn't make code any clearer.

seancorfield05:02:15

Don't get me wrong, I find (partial f a b) to be annoyingly long, as well as (comp f g), compared to Haskell's auto-currying and therefore f a b and f . g but it really isn't worth messing up the language syntax for a few characters of typing...

☝️ 10
john05:02:07

It's not about saving characters. It's about semantic context switching between various thread macro semantics

seancorfield05:02:24

We're going to disagree on that.

seancorfield05:02:51

I find this much more readable than your proposal (-> n (- 3) dec (->> (assoc state :n)) keys)

seancorfield05:02:41

Especially when written indented:

(-> n
    (- 3)
    dec
    (->> (assoc state :n))
    keys)

seancorfield05:02:01

There's also a strong argument that such long threads are inherently hard to read already and should be broken into named segments with let...

john05:02:31

hmmm. perhaps the idea could be further generalized and simplified... Perhaps -#> could just evaluate all anonymous functions defined within the thread first ... then it's just (-#> n #(- 9 % 3) dec #(assoc state :n %) keys)

seancorfield05:02:21

Personally, I think that's horribly ugly. It's not even consistent.

seancorfield05:02:52

You're mixing an implicit -> with regular anonymous functions 😞

john05:02:23

It makes sense though... It's a thread macro that auto-evaluates anonymous functions first.

seancorfield05:02:35

It doesn't make sense to me. It's horrible.

john05:02:58

I know you're just saying that to goad me into writing a library for it... And I'm not gonna do it!

seancorfield05:02:04

(as-> n % (- 9 % 3) (dec %) (assoc state :n %) (keys %)) is also horrible but at least it's consistent (and, hey, already possible/legal in Clojure).

john05:02:58

well, I guess that -#> is pointless, since it's basically just doing function application, just unwinding it, for less parenthesis

seancorfield05:02:15

Consistency has had me recently move to wrapping all the forms in ( .. ) in a threaded macro BTW.

seancorfield05:02:52

So I'm more likely to write:

(-> n
    (- 3)
    (dec)
    (->> (assoc state :n))
    (keys))

john05:02:02

interesting

john05:02:19

For visual clarity?

seancorfield05:02:33

The more code I maintain, the more consistency and simplicity have come to matter.

seancorfield05:02:07

And we're at

Clojure source 280 files 65913 total loc,
Clojure tests 320 files 22004 total loc,
these days.

seancorfield05:02:14

(nearly 88k lines... that's quite shocking to me, really, given where we were just a few years ago!)

🙂 5
john05:02:01

Yeah, that's pretty awesome

lilactown05:02:44

but I also hate nesting them. I think it's confusing

lilactown05:02:45

I like wrapping all my threaded forms in parens too

seancorfield05:02:58

No lein, no boot. Just clj and deps.edn these days too.

seancorfield05:02:27

(and, of course, Clojure 1.10 in production, and testing against 1.11 master 🙂 )

john05:02:30

I guess that last -#> I described is really comp>. It reverses the order of comp, so it reads like definition order, but allows you to thread in forms in the comp that are not functions.

seancorfield05:02:57

Like transducers? 🙂

john05:02:31

ooooh, transduce->

seancorfield05:02:39

:rolling_on_the_floor_laughing:

valerauko06:02:08

things get pretty nasty when i abuse threading macros

valerauko06:02:36

so when i notice I'm Doing It Again i rewrite the whole thing with lets to see which is simpler

Jan K11:02:49

When it comes to weird threading macros, this library is funny https://github.com/rplevy/swiss-arrows

NoahTheDuke13:02:59

My issue with using lets instead of threading macros is it allows me to write more procedural/pythonic code, instead of finding a functional way to write it

NoahTheDuke13:02:51

I’ve been working on a lot of ETL pipelines recently, so extracting my complicated let conditionals into functions I can thread has been very helpful in maintaining the functional mindset

hmaurer14:02:02

To store clojure data structures in a database (postgresql in my case), would you just store the EDN representation or encode it with transit?

mpenet14:02:44

If you dont care about querying use something like fressian or nippy

dominicm15:02:21

@hmaurer using transit json-pretty gives you the option to query it in the future if you please.

hmaurer15:02:10

what is the json pretty option?

henrik15:02:23

I think it's supposed to be read as an n/m dash rather than a hyphen.

dominicm15:02:28

We have done this on a project before.

hmaurer15:02:21

@dominicm oh I didn’t know that option, thank you

hmaurer15:02:54

When I try to require a namespace in the REPL but it fails because of a syntax error in said namespace’s file, how can I reload it after correcting the error? running require again throws a “namespace not found” error

dominicm16:02:04

@hmaurer guessing you might want to try (require 'foo :reload)

hmaurer16:02:17

@dominicm I tried that, it didn’t work :thinking_face:

kwladyka17:02:36

precise > it didn’t work :thinking_face: 😉

hmaurer17:02:42

@kwladyka I tried :reload but that didn’t work. I’ll try again

hmaurer17:02:46

it threw the same error

hmaurer17:02:50

hang on, I’ll reproduce it

kwladyka17:02:33

but normally you use editor shortcuts to do that

lilactown20:02:46

I have probably a silly question:

(let [foo (my-macro)]
  ...)
I want my-macro to know that it has been bound to the symbol foo in this context. Impossible, yes?

lilactown21:02:33

😭 thought so

lilactown21:02:12

OK, followup: is there any way (hacky or otherwise) to get at the let bindings inside of a function?

lilactown21:02:42

I have a lib where I’d like to provide some report of how it’s used inside of an opaque function

lilactown21:02:44

using some fancy tricks I can show what was passed in when the library functions were invoked, and the order of invocation, but I’m losing some semantics because there’s no names attached to them

john21:02:54

@lilactown you can get the line number with (-> &form meta :line)

lilactown21:02:27

it would still be hard to know exactly what name it was bound to, but that could be hilariously hacky

john22:02:51

you can get the namespace in there too, but yeah 😕