Fork me on GitHub
#clojure
<
2018-02-04
>
matan00:02:49

what's your best trick for when you need (key val) semantics in your code, rather than having maps with just a single map entry? maps with single-map entries require a first for every access.. or maybe they don't

joelsanchez00:02:30

aren't those just tuples? (in this case, 2-tuples represented as vectors of two items)

matan00:02:49

yep, they are

matan00:02:07

okay, so you then access the key as first and val as last

joelsanchez00:02:18

yes, and you can destructure them

joelsanchez00:02:32

in fn args, let bindings...

matan00:02:04

I'm looking for something that would give more clarity in the code, maybe destructuring would help with that, but I wish I could key to get the key, or something like that that would make code more readable and semantically clear

joelsanchez00:02:19

(let [[k v] [:key :value]]
  (println "key" k "value" v))

matan00:02:40

that's destructuring on every usage

matan00:02:03

do we have a specialized (key val) thing anywhere in clojure?

joelsanchez00:02:46

(defn give-me-a-better-name [[k v]]
  {:key k :value v})

(let [{:keys [key value]} (give-me-a-better-name [:a :b])]
  (println "key" key "value" value))

joelsanchez00:02:13

tuples are quite widespread, but maybe someone knows of smth

matan00:02:58

I'll probably do just that, only as a macro (to spare some cpu cycles)

joelsanchez00:02:22

(val (first {:a :b}))
=> :b
(key (first {:a :b}))
=> :a

matan00:02:39

that's what I have in my code now 😞

joelsanchez00:02:00

got nothing more then 😞

matan00:02:15

I should probably use a macro

matan00:02:36

that would be both readable, efficient, and kind of idiomatic as a workaround

matan00:02:30

or wear that "tuples are just data" hat and pretend the code would be readable down the road (lol)

joelsanchez00:02:19

you could possibly try a record btw

matan00:02:48

oh right, forgot about them

hagmonk00:02:22

@matan what are you trying to do exactly? You're passing around vectors containing a single map?

matan00:02:25

encoded in either vectors or maps

hagmonk00:02:58

so assuming 2-tuples, shaped like this: [ [:foobar "cheese"] [:quok "ham"] ]

hagmonk00:02:44

and you want functions that look like (defn process-tuple [verb noun] ...) or some such?

hagmonk00:02:45

if so. probably the easiest thing is to (map (partial apply process-tuple) that-vector-of-tuples)

hagmonk00:02:37

if you have maps you can pluck out the desired tuple values first:

hagmonk00:02:53

(map (comp 
    (partial apply process-tuple)
    (juxt :your-key :your-value))
  a-vector-of-maps)

hagmonk00:02:06

for maximum clojure points you can take that map, drop the a-vector-of-maps argument, and use it as a transducer with into instead

sundarj00:02:14

user=> (def tuple [:a :b])
#'user/tuple
user=> (get tuple 0)
:a
user=> (tuple 0)
:a
user=> (first tuple)
:a
user=> (second tuple)
:b
pick your poison

matan00:02:12

"pick your poison" is quite the point in this (trivial) scenario

matan00:02:40

my pick would be either a record (with object overhead) or a macro that plucks out the key and a macro that plucks out the value

matan00:02:19

a record is "safer", but alas clojure relegates its implementation to a Java object

hagmonk00:02:34

I'd steer clear of macros if your only use case is extracting values from it

hagmonk00:02:50

and, everything is a java object, not just records

matan00:02:28

right. aren't records kind of deprecated or something?

hagmonk00:02:31

macros add a layer of indirection to the code, and aren't composable

hagmonk00:02:45

records are not deprecated, you might be thinking of structs

matan00:02:52

right, history

matan00:02:10

I was kind of wondering whether macros are composable or not. are you sure?

matan00:02:28

any good example for that?

hagmonk01:02:09

it depends on what they expand to. you can't pass a macro around as if it's a function, so the moment you want to, say, throw it inside comp you're stuck

sundarj01:02:39

if you want to alias a function, you just (def new-name f), if you want to alias a macro, you have to write a whole new macro that wraps it

sundarj01:02:44

they're contagious

matan01:02:53

they do compose with each other though, don't they? (macros)

matan01:02:46

meaning that the macro reading stage will recursively apply all macros in whatever order they are "nested" in the code

matan01:02:59

well, at least that 🙂

hagmonk01:02:31

when we say they don't compose, we mean they can't be composed in the same way functions are - passed around, chained together, etc - during runtime

hagmonk01:02:16

so if you had a function that ran first on whatever argument it was given

hagmonk01:02:33

you could pass that function to map and apply it over a sequence

hagmonk01:02:00

if you had a macro that expanded to (first some-thing) where some-thing was the argument ...

matan01:02:30

listening..

hagmonk01:02:35

you could not pass that macro in to anywhere that expected a function. at evaluation time it would be expanded in place - it needs some-thing right now, not later

hagmonk01:02:08

to make that concrete:

hagmonk01:02:34

user=> (defmacro thingy [arg] `(first ~arg))
#'user/thingy
user=> (thingy [:hi])
:hi
user=> (map thingy [:hi :there])
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'user/thingy, compiling:(NO_SOURCE_PATH:4:1)

seancorfield01:02:25

@matan To put it in perspective: we have about 75,000 lines of Clojure code at work and there are only 40 macros in all of that, and they are mostly with-... context macros to save us writing anonymous zero-arity functions (thunks).

seancorfield01:02:26

(and the majority of those are in test code to provide a mock context for stuff)

hagmonk01:02:46

I have a similar ratio, I have one macro that tidies up some complicated gen-class definitions, and every single other macro starts with with- ... :)

seancorfield01:02:46

Stuff like this

(defmacro with-tmp-file [binding & body]
  `(let [~binding (File/createTempFile "image" ".jpg")]
     (try
       ~@body
       (finally
         (.delete ~binding)))))
So we can say
(with-tmp-file f
  (do-some-tests-on f))
and reduce boilerplate.

seancorfield01:02:24

The first rule of macros is "don't use them". The second rule is "use them very sparingly". 🙂 Basically, write functions first, then use macros for a little syntactic sugar. Or only use a macro where you can't write it as a function.

ttx05:02:00

This might sound stupid, but is it possible for a sorted-map-by to have multiple values with same key?

noisesmith05:02:33

no, it cannot

ttx05:02:11

But this what I got, by doing assoc-in into sorted-map-by nested inside a regular map:

{
...
 :state.camp.in-edit/script
 {:start {:on-key {:1 []}},
  :start {:default [], :on-key {:1 []}},
  :stage-2 {:default [], :on-key {:1 []}}},
...
}
😮

noisesmith05:02:13

you can have a collection under a key, a collection is a single value

noisesmith05:02:15

also, some readers (including the clojure compiler) accept :1, but it's technically illegal and other readers will error when consuming it

ttx05:02:05

Didn't know that.

noisesmith05:02:04

you're allowed to use numbers askeys

ttx05:02:10

That I know. 🙂

ttx05:02:46

But the problem is that, there are multiple :start keys under same map.

noisesmith05:02:14

oh - how are those keys generated?

noisesmith05:02:42

either this is a serious compiler bug (unlikely?) or there's something going on with non-printing characters

ttx05:02:26

Not sure. The code works fine with regular hash-maps. Problems started only when I changed into a sorted-map-by. I can show you the spec failure.

noisesmith05:02:09

wait, is the sorting function in any way non-deterministic?

noisesmith06:02:30

user=> (into (sorted-map-by (fn [_ _] (rand-int Integer/MAX_VALUE))) [[:a 0] [:a 1] [:a 2]])
{:a 0, :a 1, :a 2}

noisesmith06:02:50

it happens, but I'd consider it a user error to do so

ttx06:02:36

Stage sorter in question: :::clojure (defn script-stage-sorter [x y] (cond (= x :start) true, (= y :start) false, (= x :start-when-machine) true, (= y :start-when-machine) false, :else (compare x y))) The intent is, :start goes to the top, after that :start-when-machine, rest of them are sorted naturally.

noisesmith06:02:19

that's non-stable though, right?

ttx06:02:33

Not sure what you mean by that.

ttx06:02:06

But clearly, as you have demonstrated, it is possible to have multiple values with same key!!

noisesmith06:02:20

right, by having a broken comparator

noisesmith06:02:42

make a condition for (= x y) and have it return either x or y consistently

noisesmith06:02:50

I bet that eliminates your issue

noisesmith06:02:09

(and check it before checking the others)

ttx06:02:53

No, it doesnt. Anyway, I should've probably used a vector of 2-tuples. The intricacies of how an unstable comparator can give weird bugs should be documented somewhere. Probably in the comment section of: https://clojuredocs.org/clojure.core/sorted-map-by . Could you please? Or should it be filed as a bug?

ttx06:02:56

BTW my comparator is also broken in the sense that it returns bool in some cases and integer (using compare) in other cases.

noisesmith06:02:31

yeah - I'd change it to always return some number -1 / 0 / 1

noisesmith06:02:43

I added a comment on clojuredocs

noisesmith06:02:12

oh and when I said "returning either x or y" I should have said "return 0"

andy.fingerhut06:02:35

There is a guide page on comparators that might have some useful info: https://clojure.org/guides/comparators

andy.fingerhut06:02:37

If you had two maps that both had a :start key, the comparator function you showed above would return true when calling (compare x y), and also for (compare y x). That doesn't look good.

andy.fingerhut06:02:07

That is like saying (x < y) is true, and also (y < x) is true.

andy.fingerhut06:02:50

That is not a total order on the set of values in your domain.

ttx06:02:34

Yeah. I just read the comparator guide, and it says for boolean comparators, it checks for both equals and less than. My comparator was broken on multiple levels.

andy.fingerhut06:02:49

It is really easy to write a comparator for simple cases, but it can get surprisingly mind-bending to do it for complex data values, and still have a total order.

ttx06:02:00

The corrected one, for reference:

clojure
(defn script-stage-sorter
  [x y]
  (cond (= x y) 0,
        (= x :start) -1,
        (= y :start) 1
        (= x :start-when-machine) 1,
        (= y :start-when-machine) -1,
        :else (compare x y)))

ttx06:02:36

btw, is there any way to do syntax highlight in slack?

eskos11:02:10

When you add an actual code snippet (plus sign left to chat input box, "Code or text snippet" there's an option for selecting language which may or may not highlight syntax.

andy.fingerhut06:02:43

Also not a total order among maps that both have a :start key, for same reason as your previous one.

andy.fingerhut06:02:52

If two maps x and y both have a :start key, (script-stage-sorter x y) returns -1, and so does (script-stage-sorter y x)

andy.fingerhut06:02:16

any sorting function will get confused by such a comparator if you give it a collection with more than one value that has a :start key.

ttx06:02:15

actually, i am sorting a single map, by specifically ordering it's keys.

andy.fingerhut06:02:30

oh, sorry, I am confusing myself here.

andy.fingerhut06:02:56

you are just comparing keys only here, got it.

andy.fingerhut06:02:51

You want key :start-when-machine to always be last in the sort order, and :start to always be first? If so, that looks reasonable.

ttx06:02:23

I would like to add how big of help it was for me to have a spec of the state. 🙂 You don't figure out a map having non-unique keys without some prior experience or wasting an weekend or two! 😌

andy.fingerhut06:02:42

In case you haven't seen it, there is an open source 'ordering-map' implementation that might do exactly what you want, and is generalized to any number of custom keys that you want them to be before all others, and in the order you specify: https://github.com/amalloy/useful/blob/master/src/flatland/useful/map.clj#L243-L245

andy.fingerhut06:02:02

There is a mention of it in the Clojure cheat sheet, in the Sorted maps section

ttx06:02:02

Thanks. Will look into it.

matan09:02:33

Your advice appreciated: I spend a lot of time erring in the shape of data when processing it, especially when coming back to code written few days back. You know, getting empty collections or stack traces when my new code doesn't follow the structure of the data well. In Java/Scala this would rarely happen due to the IDE knowing my types. What would you suggest for me if you are wearing your mentor hat? it has gotten really inefficient for me, developing data-centric code, and I only program two days a week so remembering the structures by heart is not an option. The rest of us, me included, do need cognitive aids in tailoring code to data, being the fuzzy inaccurate "thinking machines" that we are. Do you have super-helpful advice wearing your mentor or experienced tutor hat?

gklijs09:02:40

@matan when I first started clojure I often made tests to check functions kept working as expected, that’s probably still a good practice. More and more I directly check in the repl.

matan10:02:31

@gklijs thanks but the tests are not my problem though 😉 I'm specifically referring to turnaround time of developing new code on top existing data, and/or modifying code in lockstep with modifying the structure of existing data.

schmee10:02:54

@matan have you looked at spec?

matan11:02:24

yes. it's one obvious thing coming to mind, but I feel this is only one ingredient for the overall workflow challenge

matan11:02:52

one thing is, it is a bit like shoehorning OO onto clojure

schmee11:02:34

I agree that it is only one ingredient, but IMO spec, tests and well-written code with good naming conventions goes a long way

schmee11:02:03

the thing that made it click for me is to use spec for describing data, not types

schmee11:02:02

I started out trying to spec functions and the return types of higher-order functions etc and thing got out of hand pretty fast

schmee11:02:06

but it’s great for describing data structures, and if you use it to spec the contents and layout of your vectors/maps etc it’s very useful and at least to me it doesn’t feel like bolted-on OO at all

matan19:02:55

@scmee just wondering quite personally, wouldn't you just use an OO language rather than spec, if you do need that sort of thing

matan19:02:28

"describing data" not "types" you mean describing content aspects beyond types?

schmee19:02:24

no, I don’t want to deal with a multitude of classes all with ad-hoc APIs, I want to use a handful of data types all over the place, and Clojure is great for that. but it can get confusing when it’s just maps being passed around everywhere and spec helps alleviate that. with spec you get both a limited set of types to deal with (maps, vecs etc) and clarity, without going full OO, and that is the sweet spot to me

schmee19:02:16

it’s a bit hard to explain what I mean by “spec data not types”, but as an example a while ago I specced a map functions to keywords. first I wrote a really complicated fdef for the functions but that started to look like haskell, so in the end I settled on (s/map-of ifn? keyword?). my point is, IMO it’s best not to use spec as some sort of type system, but limit it to describe data only

schmee19:02:27

I don’t know if any of this makes any sense 😄

matan20:02:46

Thanks, I think it makes sense

matan20:02:52

I think I'll get it when I use spec myself... abstractly it sounds like a way of lazily describing some parts of what data should be, rather than building inheritance topologies

matan20:02:26

I guess I'd still get bugs from what I do not validate over the data, or end up writing so much specs that I'd regret not using records to begin with

schmee10:02:41

an out-of-the-blue question: I’ve been reading about java.lang.invoke, and I’m curious if it could be used instead of reflection to speed up Clojure somehow?

bronsa10:02:31

indy could be used to do callsite caching for reflective calls

schmee10:02:29

okay, and I guess that java.lang.invoke is based on indy?

schmee11:02:32

invokedynamic

bronsa10:02:34

indy and java.lang.invoke are used in conjunction

schmee10:02:12

to my naive understanding, it seems like between indy and recent improvements in persistent data structures there is a lot of potential performance wins for Clojure?

bronsa10:02:34

not really

bronsa10:02:13

which improvements to pds are you talking about? the 2015 faster hamt paper?

bronsa10:02:51

indy won't help much clojure perf wise, we already have monomorphic callsite caches for most polymorphic dispatches

schmee10:02:56

https://github.com/lacuna/bifurcan seems like an interesting option for immutable data structures, although if my understanding is correct most of the performance wins compared to clojure comes from different equality and hashing schemes

schmee10:02:56

I’ve been doing some performance optimization recently and it’s got me thinking “in a perfect world, how fast can Clojure possibly get?”

bronsa11:02:14

yes re: performance wins

bronsa11:02:23

which makes them useless in practice, for clojure

schmee11:02:07

btw, monomorphic callsite cache = MethodImplCache?

bronsa11:02:53

that's a cache for multimethod dispatch values

bronsa11:02:24

the clojure compiler emits bytecode callsite caches for protocol methods and keyword lookups

bronsa11:02:20

multimethods don't have callsite caching

bronsa11:02:31

but they could, and indy would help with that

bronsa11:02:45

but it would be just mostly a comodity thing

schmee11:02:07

ahh, is registerKeywordCallsite et. al the right trail to follow?

schmee11:02:29

if I want to look at the implementation of the caches

schmee11:02:57

great! 👍

schmee11:02:59

thanks a lot for all your help and explanation, really appreciate it 🙂

schmee11:02:11

good stuff, I’m sure it will help my understanding a lot if I compare implementations in tools.analyzer and the Java compiler side-by-side

qqq14:02:14

(when-let [[a b] nil] 
  2)
(when-let [[a b] [2 3]]
  (+ a b))

when binding multiple values with when-let, how does it decide to execute the body or to execute nil ?

sundarj14:02:02

it always uses the right-hand side as the test:

user=> (source when-let)
(defmacro when-let
  "bindings => binding-form test

  When test is true, evaluates body with binding-form bound to the value of test"
  {:added "1.0"}
  [bindings & body]
  (assert-args
     (vector? bindings) "a vector for its binding"
     (= 2 (count bindings)) "exactly 2 forms in binding vector")
   (let [form (bindings 0) tst (bindings 1)]
    `(let [temp# ~tst]
       (when temp#
         (let [~form temp#]
           ~@body)))))
nil

qqq14:02:39

got it, so the key is the (when temp#, which means 1. eval RHS side, store in temp# 2. (when temp#` 3. do destructuring inside that thanks!

sundarj14:02:13

no prob 🙂

qqq15:02:39

so I'm reading the JVM bytecode spec. Instructions like iload / istore / fload/fstore take a single BYTE as an argument. Does this mean that for JVM functions, # input args + + # locals <= 256 ? I don't think I've ever run into this issue in practice, but 256 actually seems low

eskos18:02:58

And yes, it is limited to 255.

qqq18:02:32

Is there a shorter way to write ((juxt #(map first %) #(map second %)) lst)

qqq18:02:28

[(map first lst) (map second lst)] is actually shorter

noisesmith18:02:13

(apply map vector (map (juxt first second) lst)) is another option

joelsanchez18:02:19

cljs.user=> ((juxt #(map first %) #(map second %)) [[:a :b] [:c :d]])
[(:a :c) (:b :d)]
cljs.user=> (seq (zipmap [:a :b] [:c :d]))
([:a :c] [:b :d])
cljs.user=> 

qqq18:02:54

is the zipmap solution correct? we need to go from LIST-OF-PAIRS to PAIR-OF-LISTs

joelsanchez18:02:24

it won't work with more than two, forget me

motoom18:02:27

and what if you want an even more generic solution? (f [[:a :b :x] [:c :d :y] [:e :f :z]]) => [(:a :c :e) (:b :d :f) (:x :y :z)]

noisesmith18:02:31

(partial apply map vector) does that

noisesmith18:02:13

+user=> ((partial apply map vector) [[:a :b :x] [:c :d :y] [:e :f :z]])
([:a :c :e] [:b :d :f] [:x :y :z])

motoom18:02:31

Beautiful.

noisesmith18:02:20

or just (apply map vector coll) if you just need to do it once and are not trying to define it as an operation

matan18:02:42

Anyone there for minor support regarding emacs + cider? I've familiarized with and tweaked emacs and installed cider. What I don't get is why sometimes the cider and repl menus show and sometimes not.

matan18:02:26

Is that likely a setup issue? is there any cider related channel out there somewhere?

the2bears18:02:25

The #cider channel would be a good place for that @matan

dominicm19:02:30

I feel like I'm having a mental block. How can I go from

{:a [:x :y]
 :b [:x :y]}
To
[[:a :x] [:a :y] [:b :x] [:b :y]]
?

Alex Miller (Clojure team)19:02:49

(into [] (mapcat (fn [[k vs]] (map #(vector k %) vs))) the-map)

greglook19:02:24

hah beat me to it: (mapcat (fn [[t xs]] (map #(vector t %) xs)) v)

greglook19:02:05

needs into or vec to result in a vector though

Alex Miller (Clojure team)19:02:39

(reduce-kv (fn [c k vs] (into c (map vector (repeat k) vs))) [] the-map)

nathanmarz19:02:23

@dominicm with specter: (select [ALL (collect-one FIRST) LAST ALL] data)

dominicm19:02:16

I must admit, this is one of the cases where the specter solution is more bewildering to me.

dominicm19:02:59

I always feel weird using a nested map, I always assumed there was a more straightforward function lying in core.

nathanmarz19:02:48

some light benchmarking shows the specter version to be the fastest as well

user=> (time (dotimes [_ 1000000] (select [ALL (collect-one FIRST) LAST ALL] data)))
"Elapsed time: 751.67376 msecs"
user=> (time (dotimes [_ 1000000] (reduce-kv (fn [c k vs] (into c (map vector (repeat k) vs))) [] data)))
"Elapsed time: 1065.379155 msecs"
user=> (time (dotimes [_ 1000000] (into [] (mapcat (fn [[k vs]] (map #(vector k %) vs))) data)))
"Elapsed time: 1036.123062 msecs"

qqq20:02:16

(vec (for [[k vs] m, v vs] [k v]))
do others hate for loops ?

noisesmith20:02:44

for is not a loop, it's a list comprehension

qqq20:02:16

you're right, for some reason, it always registers as a "lazy for loop" for me

qqq20:02:05

anyway, for is one of my favorite macros, especially when used with :when and :let , all types of magic

jmromrell20:02:42

Hey, I just integrated steam authentication (using OpenID) into an app I am working on. I'm trying to understand how to securely keep track of this across future requests. Ring has sessions, but it looks like they end up getting stored on the client via cookies or memory-stores (thus tamperable?)

jmromrell20:02:11

Do clients only get a copy of the session values? Or is it stored exclusively client-side?

noisesmith20:02:28

@jmromrell the default session is a token in a cookie, mapped to the actual data in the server memory

noisesmith20:02:51

to securely store data in the client, and verify it came from your server, a decent option is JWT stored in localstorage

jmromrell20:02:29

As long as the data is stored in non-tamperable form on the server (which you suggest sessions do?), that is sufficient for me

greglook20:02:37

signing the authenticated principal (the steam account ID) into a JWT and using that as a bearer token is pretty standard pattern

qqq20:02:46

(let [tag ...]
  (case tag
    a1 (f tag)
    a2 (f tag)
    a3 (f tag)
    b1 (g tag)
    b2 (g tag)
    b3 (g tag)))
^-- is there a way to simplify this ?

greglook20:02:44

(let [tag ...]
  (case tag
    (a1 a2 a3) (f tag)
    (b1 b2 b3) (g tag)))

greglook20:02:07

@qqq group the cases with a list

noisesmith20:02:12

@jmromrell right, but it doesn't expand to multiple servers (or even persist across server restarts), which is why JWT starts to look more attractive

noisesmith20:02:45

the usage of parens for grouping in case is one of my few pet peeves about clojure syntax - it's inconsistent with other usage of parens, and it would be nice to be able to tell new users "parens are not for grouping"

jmromrell20:02:58

In this case, I think I can plan on the app remaining on one server for the foreseeable future. I think I'll stick with sessions and keep JWT in mind if needed in the future

jmromrell20:02:03

Thanks for the help 🙂

greglook20:02:38

yeah case is a bit of an idiosyncratic corner, but since the tests must be compile-time constants it’s already a bit different than cond and friends.

qqq20:02:43

@noisesmith: `(case tag a 2)`` also confuses me, I don't expect it to amtch tag vs 'a, but I expect it to amtch tag vs value of a in local env

noisesmith20:02:27

right, this is a problem with macros in general though, it's hardly limited to case - let and def do the same thing for starters

qqq20:02:51

well, I expect let to do what it does, as I"m binding the stuff on the lhs

noisesmith20:02:33

yes, it feels different, but it comes from the same underlying power of macros / special forms

alexstokes21:02:06

does anyone have a rationale for why some of the clojure.core functions like map take the collection at the end of the arglist and others like assoc take the data as the first parameter? until you learn the APIs I find having to toggle between -> and ->> for instance and it would be nice if it was uniform

Alex Miller (Clojure team)21:02:15

it is uniform but there are two things going on here, not one

Alex Miller (Clojure team)21:02:44

assoc is a collection function - it takes a collection and returns a function. all collection functions take the coll first

Alex Miller (Clojure team)21:02:10

map is a sequence function - it takes a sequence (really a seqable) and returns a sequence (really a seqable)

Alex Miller (Clojure team)21:02:18

those always take the sequence last

alexstokes23:02:59

thanks — any reason in particular that sequence functions take the sequence last? why not first?

qqq23:02:51

(def my-map #(map %2 %1))

qqq23:02:50

(defn flip [f] #(f %2 %1)) <-- then, one can flip all the seq functions

avfonarev23:02:32

Can anyone explain to me the difference between a namespaced keyword and a keyword :person/name, where there is no namespace person defined?

noisesmith23:02:48

:person/name is still namespaced - the namespacing of the keyword doesn't mean it belongs to a namespace, it means it has a namespace part

noisesmith23:02:04

=> (namespace :person/name)
"person"

avfonarev23:02:49

This is what I checked indeed. However, I got confused by an article recently posted on reddit (https://blog.jeaye.com//2017/10/31/clojure-keywords/) which states that :person/name should be mostly avoided.

noisesmith23:02:38

:person/name is still namespaced, the distinction that @jeaye defines there is not standard, it's one that he is making - he's arguing it's useful to avoid keywords that don't map to specific namespaces, but there's no implementation difference and it's not something believed in or enforced by the language

jeaye23:02:09

Indeed. "Syntactically, grouped keywords are namespaced keywords, but they’re not tied to a valid namespace."

avfonarev23:02:25

@noisesmith Thank you! For instance, you answered my next question about implementation details.

qqq23:02:50

This is dynamically generated, so I can't just do (MyClass/foo x y z) I have

class-name = "MyClass"
static-func-name = "foo"
how do I call function static-fun-name of class class-name with args x, y, z ?

moxaj23:02:39

@qqq reflection. Class/forName, getMethods, etc.