Fork me on GitHub
#clojure
<
2021-03-04
>
simongray14:03:03

I want to reuse some variadic keywords args that are destructured in one function - i.e. a map m - when calling another function f that also takes variadic keywords args. Is the idiomatic way to do this then

(apply f (mapcat identity m))
? Applying the map directly doesn’t work, so I have to flatten it somehow which I do using (mapcat identity m).

noisesmith19:03:55

also consider mapcat seq or apply concat in place of mapcat identity

borkdude14:03:33

yeah, this is an annoying problem that might be addressed in clojure 1.11 but over the years the community has moved towards an options map instead of keyword opts (at least, in my perception)

reefersleep08:03:37

Count me as one of the community in this matter. Variadic keyword arg destructuring seem stunted, as demonstrated here.

Alex Miller (Clojure team)14:03:28

(will be addressed in 1.11 and there may be multiple reasons this pendulum will swing the other way)

metal 15
👀 3
vlaaad14:03:17

jokes on you, I use both at the same time already

borkdude15:03:29

My Clojure goes to 11

roklenarcic16:03:01

Are there a functions in clojure core that would behave like and and or but not a macro, a function and I don’t need the short circuit logic?

roklenarcic16:03:17

I find myself often in need of (map and seq1 seq2) and or version of such

borkdude16:03:07

every? identity for example

enn16:03:18

or some identity for or

Alex Miller (Clojure team)16:03:30

#(and %1 %2) for that case?

lilactown16:03:01

I didn't realize that and and or aren't provided as fns along side their macro form

bronsa16:03:33

clojure doesn't have separate namespaces for fns and macros, a var is either a fn or a macro

lilactown16:03:26

clojurescript does which is maybe why I was confused

borkdude16:03:41

that's only done for optimizations though

bronsa16:03:43

yeah, cljs has a completely different compilation model than clj

bronsa16:03:00

@U04V15CAJ not really, userspace code can do this freely afaict

borkdude16:03:26

I mean, they are used for optimizations in cljs

borkdude16:03:48

The occurrence of macros and functions for the same var

bronsa16:03:17

the cljs impl uses this mechanism for optimisations, sure, but it's completely open

borkdude16:03:34

yes, I was speaking about clojurescript macro-functions specifically

borkdude16:03:27

maybe the word "only" wasn't appropriate, sorry

bronsa16:03:43

tbh it should be only done for optimisations and limited to core.

bronsa16:03:16

I'm not a fan of this working for user code at all

lilactown16:03:48

to my mind macros are used for performance and the functions are provided to allow the semantics that roklenarcic is talking about (passing it into apply/map/etc.)

bronsa16:03:48

but then, as I mentioned, clojure technically does have the same capability via the impl details of definline

bronsa16:03:04

no, that's completely wrong

bronsa16:03:12

macros are used for changing evaluation semantics

bronsa16:03:34

it's incidental that in some cases controlling the evaluation semantics can lead to better performance hints to the compiler

borkdude16:03:54

In general yes, but e.g. the + macro is added for optimizations, while it's also a fn. this is what I was referring to

lilactown16:03:17

I wasn't making a strong claim of "macros are ONLY used for performance"

bronsa16:03:21

yes but that could be done (and imo, should be!) as an intrinsic to the compiler instead of as a macro

bronsa16:03:34

clojure has similar optimisations baked into the compiler

bronsa16:03:39

¯\(ツ)

borkdude16:03:10

cljs.user=> (macroexpand '(+ 1 2 3))
(js* "(~{} + ~{})" (cljs.core/+ 1 2) 3)
cljs.user=> (macroexpand '(apply + [1 2 3]))
(apply + [1 2 3])

bronsa16:03:31

yeah I'm aware of how it's done in the cljs impl :)

bronsa16:03:50

@U4YGF4NGM no but it's wrong to say for example that " and as a macro is used for performance and then a fcuntion for semantics"

borkdude16:03:01

ah so that is called an "intrinsic", now I get what those compiler people were talking about

simple_smile 3
bronsa16:03:20

this can only be true for a very limited subset of "functions" that have function semantics

bronsa16:03:26

and doesn't have function semantics

lilactown16:03:26

anyway, I've used this to effect in some libraries where I wanted to provided a macro - for some performance reasons - and also a function to allow function application and other more dynamic uses. I would be sad to lose that capability

borkdude16:03:52

in Clojure we don't have this capability and I've never used it in CLJS

bronsa16:03:07

eeeh we kinda do :) it's just not very known

borkdude16:03:13

(I went to some compiler-related conference last weekend)

borkdude16:03:25

you mean (definline ...) ? ok, never used it

bronsa16:03:12

user=> (defn foo {:inline (fn [x] (+ x 1))} [x] (- x 1))
#'user/foo
user=> (foo 2)
3
user=> (apply foo [2])
1

bronsa16:03:17

close your eyes and forget you ever saw this

borkdude16:03:02

ah that one, yes I knew it, but I usually roll a macro and just don't allow myself to use it as a fn when I want this ;)

bronsa16:03:25

@U4YGF4NGM the capability is sound, other lisps have similar things -- I just don't think having this macro+fn hack is the way to do it, but whatever

lilactown16:03:45

I don't really understand the what/why of some of the things you're saying. I am trying to participate in the discussion to explain my experience using macro+fn, and you tell me very bluntly that I'm "wrong"

bronsa16:03:18

I was just referring to this statement to my mind _macros_ are used for performance

lilactown16:03:37

let me amend

lilactown16:03:56

to my mind macros _are used sometimes for performance_

bronsa16:03:20

ish :) but I don't want to nitpick

lilactown16:03:45

I feel like I have spent a lot of energy very quickly here on an argument that we never needed to have

bronsa16:03:13

I'm sorry you felt we were having an argument, not my intention

borkdude16:03:59

Macros are always a sensitive topic. ;)

borkdude16:03:56

Like lisp-1, lisp-2, and why Clojure is not a true lisp-1, etc ;P

bronsa16:03:11

I have a bit of a gripe with some unnecessary "extensions" to the evaluation model of cljs compared to clj, this is part of it

borkdude16:03:39

So what other "extensions" are there?

bronsa16:03:54

shoulda said changes rather than extensions but off the top of my head: type hints having different semantics (regardless of the absence of classes in js), clj/cljs namespace renaming, metadata evaluation being different, different arglists quoting, overuse of js* as an escape hatch in the impl

bronsa16:03:10

things that matter more from a lang impl/analysis tooling PoV than end-user experiene anyway

borkdude16:03:24

I had an issue once with a macro that used try/catch with some code from cljs.spec (which is also macro-rich) but ran into the issue that the catch or finally clause was analyzed before the body of try

borkdude16:03:51

and this wasn't considered an issue, so I had to come up with an ugly workaround

borkdude16:03:10

the issue was that cljs.spec macros do side effects during macro-expansion

borkdude16:03:17

so the side effects happened at the "wrong" time

bronsa16:03:43

lol, yeah, I wish we could have the invariant that macros aren't side-effectful

bronsa16:03:12

t.a.jvm has to take a non insignificant performance hit to handle this

borkdude16:03:22

it's comparable to the ns macro, or def special form, it has the side effect of defining something in the runtime before the rest is analyzed

bronsa16:03:21

yeah there's a bunch of macros in the wild that do that

bronsa16:03:38

whatever's the name of that testing framework, also does it in clj

borkdude16:03:38

I remember slipset suggesting an optimization for this related to eastwood, right?

bronsa16:03:50

yeah it's the ns-safe-macro check

borkdude16:03:55

clojure.test?

bronsa16:03:59

nah the ugly one :)

bronsa16:03:06

there it is

bronsa16:03:10

we don't speak its name

borkdude16:03:18

you said ugly, that rang a bell ;P

borkdude16:03:20

honestly I have no experience with this framework, so I don't want to say anything bad of it, I've only heard some people (e.g. circleci blog) complain about it

bronsa16:03:47

can't be both at the same time

roklenarcic16:03:01

for instance something like:

(every? (fn [[x y]] (< x y)) [1 2 3] [3 4 5])

bronsa16:03:19

(modulo definline stuff, but let's pretend that doesn't exist)

roklenarcic16:03:47

so I tried doing (map < [1 2 3] [3 4 5]) but then there’s no elegant way to roll those booleans up

borkdude16:03:25

reduce #(and %1 %2)

borkdude16:03:45

or use (reduced ...) for short-circuiting

roklenarcic16:03:57

yeh… that’s why I asked if there’s a function for that, I know I acan use anon function and call and in it

borkdude16:03:02

but this is similar to (every? identity ...)

roklenarcic16:03:25

thanks borkdude, I’ll guess I’ll use every?… but this same thing came up many many many times in various code, where and or or would be perfect as an argument to coll processing function and then I have to go #(and %1 %2)

borkdude16:03:06

make a proposal in an ask.clojure issue :)

phill16:03:32

@alexmiller I seem to recall CLJ-2590 came up many years ago and the sticking point was a Windows limit on the length of a fully qualified class name

Alex Miller (Clojure team)17:03:08

not sure what the question is

phill22:03:36

CLJ-2590 is the question and I think the answer was, If we 'fix' it by composing internal class names from symbols instead of numbers then the resulting increase in class name length will cause some code that currently works in Windows to not work any more.

phill22:03:47

I think it was discussed on the Google group.

Elso16:03:21

I think this is a fairly trivial thing to achieve, but I'm kind of lost how to do this without any state atm I have a bunch of maps which I group with group-by and then I need to map over the map and add an id from a lazy seq of ids to each of the maps in the respective groupings however when I do this with (repeatedly (rand-int 1000)) and (take (count grouped-maps) ids) I have the issue that all records get ids from the same starting point I mean, I could of course modify the id sequence, but I'd like to know how to do this idiomatically So somewhat like this: (->> stuff (group-by :some-key) (map (fn [[k v]] [k (map (fn [[m id]] (assoc m :id id)) v ids)])))

p-himik17:03:46

It can be exactly almost like this if you replace [[m id]] with just [m id]. map can accept any number of collections to map over.

flowthing17:03:32

Do you need to group before adding :id? It's much easier if you group afterwards:

(def ids (iterate inc 1))
(def things [{:a 1 :b 2} {:a 1 :b 3} {:a 2 :b 4}])
(group-by :a (map (fn [id thing] (assoc thing :id id)) ids things))

3
3
👍 3
wombawomba17:03:25

What OIDC client library should I be using in a Clojure project?

afry20:03:17

Does anybody have link to a good example repo for JWT authentication using Buddy that they can share? https://github.com/funcool/buddy I'm trying to follow along with the example code here: https://github.com/funcool/buddy-auth/blob/master/examples/jws/src/authexample/web.clj Although it isn't quite complete enough to see the whole process (authenticate with username and password, generate token, pass token back to web client, authenticate protected routes using token)

p-himik22:03:00

Why JWT in particular - can't regular session IDs be used?

afry22:03:16

I've got to authenticate across multiple domains, and as I understand it JWT is the way to go when that's the case.

p-himik23:03:46

You mean, like SSO? So you login to example1 and you would be automatically logged into example2?

p-himik23:03:21

JWT might be needed when you have a stateless backend or a distributed backend where it's unfeasible to share the state between all the instances. In all (or perhaps almost all) other situations regular session IDs are not only sufficient but also better. They're as simple as it can get and yet can provide all the security you will need.

borkdude22:03:54

Can you have in general a defrecord/deftype or reify that implements two different interfaces / protocols with clashing method names?

borkdude22:03:25

or can you uniquely identify the interface / protocol by the method name?

borkdude22:03:52

no to which question?

ghadi22:03:58

the second one

ghadi22:03:30

IIRC the JVM doesn't take into account in the interface in the method definition

borkdude22:03:17

What I mean is: can you construct a reify with two method names from different interfaces?

ghadi22:03:43

two identical names? no

borkdude22:03:48

two identical yes

ghadi22:03:50

they'll be the same impl for both interfaces

ghadi22:03:50

(assuming the args & return type are all the same, too)

borkdude22:03:05

what if the arities are different (not sure if Clojure supports types in the signature of those?)

ghadi22:03:14

you could REPL it

borkdude22:03:03

yeah, trying it

borkdude22:03:54

So I've got this:

user=> (definterface Foo (foo [_]))
user.Foo
user=> (definterface Bar (foo [_ _]))
user.Bar
user=> (def x (reify Foo (foo [_ _] :foo) Bar (foo [_ _ _] :bar)))
#'user/x

ghadi22:03:04

user=> (definterface One (foo [x]))
user.One
user=> (definterface Two (foo [x y]))
user.Two
user=> (def r (reify One (foo [_ x] :one) Two (foo [_ x y] :two)))
#'user/r
user=> (.foo r 42)
:one
user=> (.foo r 42 42)
:two

ghadi22:03:12

lol, are we twins? 🙂

borkdude22:03:58

user=> (.foo x 1)
:foo
user=> (.foo x 1 2)
:bar 
Ok, that answers my question, thanks!