clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Busy escaping the comfort zone 2021-04-05T02:19:53.281200Z

Hi Clojurians, I'm having issues with clojure.tools.namespace.repl/refresh and jdk versions > 8, I'm using Amazon correto JDK and was able to confirm that clojure.tools.namespace.repl/refresh doesn't work in any correto version > 8: • Amazon correto 8 -> working • Amazon correto 11 -> not working • Ubuntu OpenJdk 11 -> working • Amazon correto 16 -> not working Iv also found https://clojure.atlassian.net/browse/TNS-54 which seems to have been resolved a while ago, any idea what the issue can be? Thanks

alexmiller 2021-04-05T02:22:30.281700Z

“Doesn’t work” is not too helpful

alexmiller 2021-04-05T02:22:59.282500Z

“I do x and expect y but see z” would be more useful

2021-04-05T02:27:29.285400Z

Does the code load without reloading on those different jdks?

Busy escaping the comfort zone 2021-04-05T02:30:29.287800Z

Hi @alexmiller happy to add more details 🙂 iv used this project https://github.com/re-ops/re-share and ran the following:

$ git clone <https://github.com/re-ops/re-share>
$ cd re-share
$ lein repl 

; In cases it did work the expected result (Amazon correto 8 and Openjdk 11)
re-share.config.secret=&gt; (clojure.tools.namespace.repl/refresh)
:reloading (re-share.oshi re-share.wait re-share.core re-share.zero.keys re-share.config.core re-share.schedule re-share.es.common re-share.encryption re-share.config.secret re-share.spec re-share.zero.common re-share.log user re-share.zero.events)
:ok

; In cases it didn't work (Amazon correto jdk 11/16)
re-share.config.secret=&gt; (clojure.tools.namespace.repl/refresh)
:reloading ()
:ok
Iv also made sure that I'm using the latest namespace library:
$ lein deps :tree | grep namespace

....
 [org.clojure/tools.namespace "1.1.0" :scope "test"]
lein deps :tree | grep classpath 
...

   [org.clojure/java.classpath "1.0.0" :scope "test"]

Busy escaping the comfort zone 2021-04-05T02:31:06.288400Z

The above did re-produce for me in one other project

2021-04-05T02:31:36.288600Z

I don't using reload a ton, but I have used it on a number of different correto installs just fine, and it isn't a particularly complex thing,

2021-04-05T02:32:00.289Z

So my guess would be the behavior is the result of something else

Busy escaping the comfort zone 2021-04-05T02:32:17.289300Z

Hi @hiredman did it work on versions 11/16?

Busy escaping the comfort zone 2021-04-05T02:32:37.289700Z

I don't mind trying a clean project from scratch it could be that I'm missing something

vemv 2021-04-05T02:35:52.290500Z

have you tried it having performed set-refresh-dirs beforehand? that way it won't try traversing the classpath

vemv 2021-04-05T02:36:18.290900Z

(set-refresh-dirs is very recommendable for other reasons anyway)

2021-04-05T02:37:20.291Z

My immediate guess is it has something to do with the user.clj you have in dev/ without listing dev/ in the project.clj anywhere

2021-04-05T02:38:16.291100Z

But it is tricky, with lots of lein plugins, and potentially more in your profile.clj to untangle this kind of thing

2021-04-05T02:41:29.291200Z

So the first thing is to disable all the plugins, both in the project and any user level stuff. Then get rid of the user.clj file, then see what is going on

Busy escaping the comfort zone 2021-04-05T02:43:03.292700Z

Ok iv confirmed this on a clean project (https://github.com/narkisr/test) Using two different jdk versions:

$ lein repl 
1
nREPL server started on port 33397 on host 127.0.0.1 - <nrepl://127.0.0.1:33397>
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 11.0.10+9-Ubuntu-0ubuntu1.20.04
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

test.core=&gt; (require 'clojure.tools.namespace.repl)
nil

test.core=&gt; (require 'clojure.tools.namespace.repl)
nil

test.core=&gt; (clojure.tools.namespace.repl/refresh)
:reloading (test.core test.core-test)
:ok

test.core=&gt; Bye for now!
$ lein repl 
nREPL server started on port 36563 on host 127.0.0.1 - <nrepl://127.0.0.1:36563>
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 11.0.10+9-LTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

test.core=&gt; (clojure.tools.namespace.repl/refresh)
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:471).
clojure.tools.namespace.repl

test.core=&gt; (require 'clojure.tools.namespace.repl)
nil

test.core=&gt; (clojure.tools.namespace.repl/refresh)
:reloading ()
:ok

Busy escaping the comfort zone 2021-04-05T02:43:40.293200Z

I will remove my profile.clj next

vemv 2021-04-05T02:43:45.293400Z

> So the first thing is to disable all the plugins, both in the project and any user level stuff. Then get rid of the user.clj file, then see what is going on fwiw, it looks fairly safe to me those plugins aren't exactly wild "dev" seems missing from the :source-paths and there's set-refresh-dirs missing

Busy escaping the comfort zone 2021-04-05T02:44:14.294Z

The above project doesn't use user.clj just empty project calling the function directly

2021-04-05T02:44:23.294300Z

The confirm on a clean project is not correct

2021-04-05T02:44:37.294600Z

You didn't do the same thing in both repls

2021-04-05T02:45:09.295Z

Your second attempt is missing the require to load tools.namespace

vemv 2021-04-05T02:45:21.295300Z

https://github.com/stuartsierra/reloaded uses exactly my advice and comes from the t.n author. I recommend you to save time and not go overboard with scientific debugging

Busy escaping the comfort zone 2021-04-05T02:48:49.297600Z

Ok, I think I got a lead removing my profiles.clj did work:

{
    :user {
    :plugins [
       [mvxcvi/whidbey "2.2.0"]
       [io.aviso/pretty "0.1.37"]
       [cider/cider-nrepl "0.22.4"]]
  
      :middleware [
        cider-nrepl.plugin/middleware
          io.aviso.lein-pretty/inject
          whidbey.plugin/repl-pprint]
  
      :source-paths  ["dev" "test"]
  
    :dependencies [[io.aviso/pretty "0.1.37"]]
   ...
  }
$ lein repl 
nREPL server started on port 45839 on host 127.0.0.1 - <nrepl://127.0.0.1:45839>
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 11.0.10+9-LTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

test.core=&gt; (require 'clojure.tools.namespace.repl)
nil
test.core=&gt; (clojure.tools.namespace.repl/refresh)
:reloading (test.core test.core-test)
:ok
test.core=&gt; 
My only guess is that one of the above deps is triggering this, ill continue to debug but this doesn't seem to be a core issue 🙂 Thank you @alexmiller @hiredman @vemv

vemv 2021-04-05T02:49:49.298100Z

my bet is cider-nrepl :) https://github.com/clojure-emacs/cider-nrepl/pull/668

Busy escaping the comfort zone 2021-04-05T02:51:04.298300Z

Thanks! the fact that it did work on some JDK but not on others threw me off course

vemv 2021-04-05T02:52:50.298700Z

🙌 again, reading the t.n source I think that performing set-refresh-dirs would remove this point of friction altogether

Busy escaping the comfort zone 2021-04-05T02:59:41.298900Z

Cool, ill take a look into that, thanks again

🙂 1
2021-04-05T09:50:10.302600Z

I'm using some library that gives names that include unicode symbols to some of it's vars. It works fine when I run it on my own machine but elsewhere it throws a ClassNotFoundException when it tries to invoke one of these variables. Is there an environment variable I should be setting to ensure this works everywhere?

Endre Bakken Stovner 2021-04-05T11:26:52.308700Z

I want selmer.parser/render (https://github.com/yogthos/Selmer) to act like the following:

(render "{{input}}" {:input ["a.txt" "b.txt"]}) =&gt; "a.txt b.txt" ;; does not work
(render "{{input.1}}" {input ["a.txt" "b.txt"]}) =&gt; "a.txt" ;; works
Currently the first line would print:
(render "{{input}}" {:input ["a.txt" "b.txt"]}) =&gt; [&amp;quot;a.txt&amp;quot; &amp;quot;b.txt&amp;quot;] 
since selmer just calls toString on each object in the map. I think I could achieve what I want by creating a custom veclike object with a toString-method like this: (toString [self] (str/join " " self). But then the question is: How do I create a veclike object?

rutledgepaulv 2021-04-06T14:07:55.087500Z

why would you not just use the join filter for the first case? https://github.com/yogthos/Selmer#join

p-himik 2021-04-06T14:10:36.088300Z

I didn't know about that filter (not that familiar with Selmer at all), but FWIW I think it's the best solution.

p-himik 2021-04-05T12:15:31.309200Z

Do you plan to use that custom vector right when passing the arguments to render, or somewhere up the callstack?

Endre Bakken Stovner 2021-04-05T12:16:11.309400Z

Only in render 🙂

p-himik 2021-04-05T12:17:19.309600Z

Then it will be both easier and simpler to just pre-process :input the way you need. E.g.:

(let [input (str/join " " ["a.txt" "b.txt"])]
  (render ...))

Endre Bakken Stovner 2021-04-05T12:18:44.309800Z

That would break case 2 above:

{{input.1}}
would now become a not a.txt.

p-himik 2021-04-05T12:19:35.310Z

Ah, you want both to work with the very same :input value?

Endre Bakken Stovner 2021-04-05T12:19:58.310200Z

I cross posted this (see https://clojurians.slack.com/archives/C053AK3F9/p1617618214113600) since no-one in beginners knew how to do it. I guess it is not a newb q.

p-himik 2021-04-05T12:20:47.310500Z

It is not, you're right. :) And I have a strong suspicion that it might be an instance of https://en.wikipedia.org/wiki/XY_problem

➕ 1
p-himik 2021-04-05T12:22:09.310800Z

If you have two calls to render, just as in your example, I would simply pre-process :input for the one that doesn't use indexing. If you have both {{input}} and {{input.1}} in the same template, then I'd just add an extra parameter - either something like input-joined or input-1, and pass the new value along with the original input.

p-himik 2021-04-05T12:23:01.311Z

The only scenario where you might need to go with a custom vector, is when you have no control over the template, and the template itself for some reason uses both {{input}} and {{input.1}} (which doesn't make much sense)`.

Endre Bakken Stovner 2021-04-05T12:24:15.311200Z

That will not work in my case: https://clojurians.slack.com/archives/C053AK3F9/p1617624403122600?thread_ts=1617618214.113600&amp;cid=C053AK3F9

p-himik 2021-04-05T12:26:03.311500Z

Yeah, I just got down to that message. :) I would use arg-name for a vector and arg-name-str for a string. Anything else would create implicit non-intuitive behavior that doesn't have the amount of value-add that justifies it, IMO.

Endre Bakken Stovner 2021-04-05T12:27:19.311700Z

But if you were to do it?

Endre Bakken Stovner 2021-04-05T12:27:27.311900Z

How would you do it?

p-himik 2021-04-05T12:27:29.312100Z

> users would have to write it all the time It's not a bad thing. I would just figure out what the least frequent scenario is, and change it (so either -vec or -str, but not both).

p-himik 2021-04-05T12:30:14.312300Z

If I had no control over the template and it used {{input}} for implicitly joined strings and {{input.1}} for nth, I would write that template library maintainer and ask for the reasoning behind it. I'd try to discuss with them if there's maybe a better alternative that's backwards-compatible and doesn't have an implicit behavior. If that would prove to not be fruitful, I would search for a different library or write one myself. I might be biased a bit, but I have had to deal with enough of implicit magic behaviors to justify my aversion towards them.

Endre Bakken Stovner 2021-04-05T12:30:40.312500Z

Hmmm, I guess vec would be the least common case. And avoiding magic is one of the reasons I like clojure to begin with. I was just trying to copy behavior from snakemake I had gotten used to.

p-himik 2021-04-05T12:32:57.312700Z

So many people have used to sh and bash. And they are just so horrible. :D No offense meant to anyone is meant, of course.

🐟 1
🐚 1
p-himik 2021-04-05T12:40:56.313200Z

Perhaps fish is better - I've never tried it. But it can't solve the root problem of all the tooling that has been created around the same time, in the same ecosystem. E.g. any whitespace or quotes in file names can cause problems. -0 is a band-aid that can help you, but it's not great.

NoahTheDuke 2021-04-05T14:40:19.313500Z

what do you mean by "elsewhere"?

2021-04-05T15:00:26.313700Z

e.g. the official jdk base image https://hub.docker.com/_/openjdk

NoahTheDuke 2021-04-05T15:01:03.314Z

does the error show the unicode in the symbol/variable name?

2021-04-05T15:13:41.314300Z

Yeah. e.g.

1. Unhandled java.lang.NoClassDefFoundError
   geocoordinates/core$initial_latitude$calculate_φ__475

NoahTheDuke 2021-04-05T15:15:10.314500Z

according to this (https://stackoverflow.com/a/65490/3023252), you should be able to use them, which makes me think something else is happening

2021-04-05T15:17:24.314800Z

I think it's a LOCALE issue. Just trying to verify

Ben Sless 2021-04-05T17:13:25.315800Z

Is there a way to know during macro expansion if a symbol is expected to be primitive by the compiler even though it's not type-hinted?

2021-04-05T17:24:37.316500Z

the compiler doesn't look at things till after macroexpansion

2021-04-05T17:25:37.317700Z

which is to say, no, because there is no expectation at the point where macro expansion runs

Ben Sless 2021-04-05T17:26:43.318900Z

When expanding a macro inside the scope of a function with a type hinted primitive I can see in the &amp;env it's mapped to a Compiler$LocalBinding which has a jc field (Java Class) which contains long

Ben Sless 2021-04-05T17:27:00.319200Z

how unwise is it to take advantage of that?

Ben Sless 2021-04-05T17:28:25.319900Z

Besides the pending execution for black magic?

2021-04-05T17:29:27.320700Z

it depends what you mean by "take advantage", but it will likely break any other macro expanders that are not the compiler

2021-04-05T17:30:12.321700Z

(so if your editor provides some kind of macro expander functionality, or core.async's go macro, etc)

Ben Sless 2021-04-05T17:30:25.322Z

I'm experimenting with opportunistic inlining of equality checks

Ben Sless 2021-04-05T17:31:42.323100Z

For example, (= "foo" bar) can be inlined as (.equals "foo" bar). Same for keywords. Numbers are trickier

Ben Sless 2021-04-05T17:32:54.324200Z

With numbers, the only thing I can be sure of is if the argument to the function is a type hinted primitive, it would throw a runtime exception before reaching any equality check in the scope of the function if it wasn't a number anyway

2021-04-05T17:33:32.324800Z

the compiler already does a fair bit of that kind of thing if it can determine the types

2021-04-05T17:33:59.325300Z

and if it throws or not actually depends on how things are type hinted

2021-04-05T17:34:17.325700Z

in the general case type hinted code doesn't throw an exception if the type doesn't match

2021-04-05T17:34:45.326300Z

user=&gt; ((fn [^String a] a) 1)
1
user=&gt;

Ben Sless 2021-04-05T17:34:48.326500Z

Generally, no, but primitives do, because invoke calls invokePrimitive

Ben Sless 2021-04-05T17:34:55.326700Z

(only for double and long)

2021-04-05T17:35:02.327Z

correct, and that is the only case

2021-04-05T17:35:43.328Z

and there the compiler knows the type, so it is already doing a bunch of "inlining"

nilern 2021-04-05T17:35:53.328500Z

I think ClojureScript does more of that than Clojure

Ben Sless 2021-04-05T17:36:18.329100Z

Huh, here's something weird

2021-04-05T17:36:21.329300Z

the call to clojure.core/= is replaced by a call to the Util/equiv static method

2021-04-05T17:36:45.330100Z

which is the compiler may then replace with some byte code

Ben Sless 2021-04-05T17:36:45.330200Z

Decompiling:

(defn foo [^long n] (== n 1))
;;
    public static Object invokeStatic(final long n) {
        return Numbers.equiv(n, 1L) ? Boolean.TRUE : Boolean.FALSE;
    }

Ben Sless 2021-04-05T17:37:14.331100Z

On the other hand:

(defn foo [^long n] (if (== n 1) true false))
;;;
    public static Object invokeStatic(final long n) {
        return (n == 1L) ? Boolean.TRUE : Boolean.FALSE;
    }

Ben Sless 2021-04-05T17:37:23.331500Z

Why does the second get inlined and the first doesn't?

2021-04-05T17:40:49.332600Z

it is because jvm bytecode is not expression based

2021-04-05T17:42:38.333500Z

the decompiled result looks the same, but one ternary operator is the result of boxing, and one is the if

Ben Sless 2021-04-05T17:43:33.334Z

Yeah, but the first case has an extra method call

Ben Sless 2021-04-05T17:44:04.334900Z

I can share the bytecode, too, if you'd like

2021-04-05T17:44:20.335300Z

the "extra" method call is what I am referring to

Ben Sless 2021-04-05T17:44:43.335700Z

ok, sorry, missed your intention there

2021-04-05T17:45:46.336500Z

there are two different sets of byte code intrinsics the compiler has

2021-04-05T17:46:15.337Z

one is purely replace this static method call with this sequence of bytecodes

2021-04-05T17:46:47.337900Z

so the bytecodes need to produce the exact same result as calling the static method would

2021-04-05T17:47:20.338900Z

the other is when compiling an if, if the test is a a matching static method, replace it with this series of byte codes

nilern 2021-04-05T17:47:47.339800Z

Pretty much every bytecode VM (and CPU) has that thing where comparisons are branches instead of producing a value

2021-04-05T17:47:59.340200Z

correct

2021-04-05T17:48:45.341700Z

so when inlining if tests, you don't actually have to match what the static method would do, you just need to match the branching behavior

2021-04-05T17:49:06.342300Z

which is why those two sets of intrinsics are different

nilern 2021-04-05T17:49:43.343100Z

But e.g. the Lua compiler will emit the same bytecode for (== n 1) as for (if (== n 1) true false) (too lazy to write the Lua equivalents)

2021-04-05T17:51:01.343800Z

I am not saying it couldn't do the swap

2021-04-05T17:51:21.344500Z

I am saying there is a reason there are two different sets of intrinsics

2021-04-05T17:52:02.345200Z

the if intrinsics have an match for Numbers.equiv, the non-if intrinsics don't

Ben Sless 2021-04-05T17:52:34.346100Z

Because the value produced by Numbers.equiv would be different?

2021-04-05T17:53:01.346900Z

no

nilern 2021-04-05T17:53:30.347700Z

It would not be as straightforward to use the branch intrinsics for returning the boolean instead of just when the source has a branch but it would be correct and feasible

2021-04-05T17:53:52.348200Z

the non-branching intrinsics just need a different sequence of bytecode to match the behavior of Numbers.equiv outside of an if

2021-04-05T17:54:20.348600Z

but, the question as always is "why?"

2021-04-05T17:55:55.350400Z

I haven't looked at the inlining specifics in a while, it may also be the case that the if intrinsics and the non-if intrinsics can conflict

2021-04-05T17:56:17.350900Z

if you inline the non-if version, then the if inlining can't be applied

nilern 2021-04-05T17:58:13.352800Z

The intrinsics are more a case of "it would be silly not to use these specialized bytecodes if the types are known" than "let's add a bunch of optimizations, it will be awesome"

Ben Sless 2021-04-05T18:00:21.354500Z

(defn foo [^long n] (== n 1))
Doesn't match this case?

nilern 2021-04-05T18:00:29.354700Z

In Lua they have a bytecode that implements polymorphic equality directly instead of the equality operator being a function, so they must do the transformation

2021-04-05T18:01:14.354800Z

no

Ben Sless 2021-04-05T18:01:28.355Z

why not?

Ben Sless 2021-04-05T18:02:06.355200Z

Because we need to return Booleans?

nilern 2021-04-05T18:02:40.355400Z

The compiler is pretty stupid, so it only uses IFNE et al. if it is compiling an if (and maybe case, but let's not go there)

nilern 2021-04-05T18:04:12.355600Z

As I said, it would be correct and feasible to use IFNE to implement your example as well, but no one has bothered

nilern 2021-04-05T18:06:20.355800Z

When the compiler gets the correct primitive overload of the static Util.equiv perf should be fine because all dispatch is gone

Ben Sless 2021-04-05T18:07:56.356Z

That's getting into JVM call conventions which I'm far from an expert on

nilern 2021-04-05T18:08:47.356200Z

Don't freak out

nilern 2021-04-05T18:09:50.356400Z

Just read the Java in clojure.lang.Util.equiv(Object, Object) vs. e.g. clojure.lang.Util.equiv(long, long)

Ben Sless 2021-04-05T18:10:37.356600Z

I have, but there's distance from that to understanding what bytecode will run

nilern 2021-04-05T18:11:37.356800Z

That distance is pretty small, javac is quite stupid as well

Ben Sless 2021-04-05T18:12:24.357Z

Mainly, the difference between calling n1 == n2 between two longs and Util.equiv(long, long) between two longs

2021-04-05T18:12:37.357200Z

after the jit?

Ben Sless 2021-04-05T18:13:31.357400Z

I guess the JIT will eliminate the static method call?

2021-04-05T18:13:42.357600Z

yes

nilern 2021-04-05T18:14:18.357800Z

Before jitting perf is terrible anyway and the JIT will inline that call (always, I think, because the method body is so small and even static)

Ben Sless 2021-04-05T18:15:08.358200Z

So you're saying not to sweat the little things

2021-04-05T18:15:30.358400Z

they are different small things

nilern 2021-04-05T18:16:44.358600Z

Using static types to get rid of branches and virtual calls matters, but inlining by itself is not so useful because the JIT does it so much better anyway

Ben Sless 2021-04-05T18:48:07.358800Z

I recall there's a limit to the method size which can be inlined by the JIT. Will this be effected by inlining directly as == vs. equiv?

nilern 2021-04-05T18:54:22.359Z

equiv(long, long) should be below the size that is always inlined by the JIT I don't recall a max inline size, if the method is bigger like equiv(Object, Object) inlining depends on profiling data ("hotness") But if there is a big method it contains a lot of code to optimize by itself and the call overhead is not as significant (e.g. it even makes sense to call the big long FORTRAN matrix procs through FFI)

nilern 2021-04-05T18:57:46.359200Z

It is complicated, but IFNE vs. a static call to equiv(long, long) should not matter after jitting

cch1 2021-04-05T19:57:19.361500Z

Given:

(defprotocol Eligible
  :extend-via-metadata true
  (make [this] :bar))

(defn eligible? [x] (satisfies? Eligible x))

(def x (vary-meta () assoc `make (fn [_] :made)))
I was a little surprised by this:
(eligible? x) =&gt; false
Am I missing something?

jimmy 2021-04-05T19:59:15.361700Z

https://clojure.atlassian.net/browse/CLJ-2426

cch1 2021-04-05T20:03:01.362Z

Thank you very much @jimmy. I’ll follow that ticket.

marciol 2021-04-05T20:26:30.371500Z

There are several discussions about how to use the full qualified keywords, and even in Clojure Spec Rationale[1] there is a quote: > These are tragically underutilized and convey important benefits because they can always co-reside in dictionaries/dbs/maps/sets without conflict It makes me feel somewhat guilty of not understanding something very important, a thing that I must understand. I see a lot of experimentation on how to use it, with a lot of learning, but it's hard to interpret what core Clojure maintainers expect when they created the full qualified keywords. It's much easier to get a glimpse of how it helps to solve real issues with examples, so my question is: Where can we find more substantial resources with examples that cover how and when to use full qualified keywords. What kind of practical problems full qualified keywords solve, beyond the abstract notion? 1: https://clojure.org/about/spec#_global_namespaced_names_are_more_important

2021-04-05T20:40:35.371900Z

I think, basically, it is rdf

2021-04-05T20:41:14.372100Z

which I guess is clear as mud

2021-04-05T20:42:24.373100Z

to some degree, my impression, is that rhickey's preferred data model is something like rdf (or a triple store or whatever, I mean look at datomic)

2021-04-05T20:43:26.373900Z

so you can image a map a given map is a set of rdf triples, with an implicit subject

2021-04-05T20:44:44.375900Z

a big difference between rdf and something like sql is column names in sql are implicitly namespaced by the table the column appears in

alexmiller 2021-04-05T20:45:08.376900Z

What is there to say about it? Sufficiently qualified attributes allow you to assign global meaning to attributes

2021-04-05T20:45:43.378200Z

rdf doesn't have that, everything is just a soup of attibutes, so you have to do things like use urls as attribute names to avoid two "name" attributes of the same thing conflicting

2021-04-05T20:46:17.379500Z

so similarly with maps, if you want to merge two random maps together, the odds of that working with namespace qualified keys is much higher than without

marciol 2021-04-05T20:46:22.379700Z

It'd be nice if there are some books or blog posts with real examples @alexmiller, I ask for the whole community. I make an exhaustive search and just found toy examples

marciol 2021-04-05T20:47:20.381600Z

or experimentations, but it'd be nice to see if someone produced something along the lines of a real use case. Videos are welcome too.

alexmiller 2021-04-05T20:49:11.382500Z

I’m not sure what needs to be said that’s interesting

2021-04-05T20:50:39.384700Z

it is the same reason spec is the way it is, you globally say "such and such a key is such and such a thing", you don't say "in some context such and such a key is such and such a thing"

marciol 2021-04-05T20:50:44.385Z

Yes, I think that you already said all about it, I'm looking for good references of real use cases in application code.

marciol 2021-04-05T20:51:12.385700Z

I know that @wilkerlucio uses it a lot in pathom, but he maintains library code

2021-04-05T20:51:34.386500Z

being global, context free, requires disambiguation

nilern 2021-04-05T20:53:39.388300Z

In application code you can totally control the data, so it is not as useful.

2021-04-05T20:54:09.388500Z

I don't know that I agree

2021-04-05T20:54:23.388800Z

large applications deal with a lot of data

2021-04-05T20:54:40.389200Z

not only from multiple sources, but from different eons of development of the same application

alexmiller 2021-04-05T20:54:41.389300Z

most applications (uses) are not open source so this is not generally something easy to come by

👍 1
2021-04-05T20:55:02.390300Z

being able to mix and match all that is very useful

wilkerlucio 2021-04-05T20:56:50.392Z

yeah, also, its hard to live complete isolation, namespaced attributes (when big enough) remove the question of "what is this data about?", IME small programs hardly ever even think about this concern, but as it grows, and them you have many services with many teams integrating stuff, having those unambiguous tractable attributes can help tremendous

wilkerlucio 2021-04-05T20:57:10.392500Z

I think its sad our industry got so used with local (short) names everywhere, makes a pain to compose systems at large

wilkerlucio 2021-04-05T20:59:22.394800Z

I guess you mean require no disambiguation?

2021-04-05T21:00:38.395500Z

depends how you think about it

2021-04-05T21:02:18.397200Z

if you imagine names are ambiguous by default, then they must be disambiguated before they can be used unambiguously

marciol 2021-04-05T21:02:37.397800Z

I need to medidate more about it, it's like a koan that I'll suddenly solve and eventually get the fqn nirvana 😄 :person_in_lotus_position:

2021-04-05T21:02:57.398100Z

so maybe "being global, context free, requires unambiguous names"

marciol 2021-04-05T21:03:08.398700Z

makes sense

seancorfield 2021-04-05T21:04:56.401400Z

@marciol I think it’s one of those things that you don’t really see the value of until you’ve been bitten by the problem it solves. At work, we use qualified keywords a lot.

marciol 2021-04-05T21:06:22.404600Z

Yes @seancorfield, seems that we need to get the feeling after using it in anger to see the benefits and where it falls better

nilern 2021-04-05T21:06:36.405Z

If everything is just a bag of properties, what next, a bag of bytes? "Use text, that is a universal interface". Just wire up some AWK and Perl scripts. :troll:

👀 1
seancorfield 2021-04-05T21:06:45.405300Z

Perhaps this example will be motivating for you? next.jdbc qualifies column names by the table they come from when returning query results. If you join across two tables, both with an ID column and a name column, you will get two different qualified keywords and no conflict: {:author/id 1 :book/id 23 :author/name "Alex Miller" :book/name "Clojure Applied" :book/author_id 1}

seancorfield 2021-04-05T21:08:48.406300Z

Without the qualification, the author ID and book ID would collide and so would the book name and author name.

isak 2021-04-05T21:09:33.407700Z

One issue with that approach is what if there are more than one book or author in one record? Just having a "type" namespace wouldn't scale. (e.g., a book with a person that wrote it, and a person that edited it)

marciol 2021-04-05T21:11:44.409800Z

It's a nice application @seancorfield

seancorfield 2021-04-05T21:11:54.410400Z

@isak Then there would be multiple column names — a database cannot have more than one name column in a table.

nilern 2021-04-05T21:12:07.410700Z

That next.jdbc feature is the must relevant application thing I have seen but it kind of just exports the namespacing you get inside SQL

seancorfield 2021-04-05T21:12:55.412100Z

@nilern By default, yes. But you can provide your own (result set) builders that do something more sophisticated if you wish.

isak 2021-04-05T21:13:57.413600Z

@seancorfield At my work we just do JSON SQL queries instead, so what comes back from the database would be {:author {:id 1, :name "Alex"} :editor {:id 2, :name "Rich"}}

nilern 2021-04-05T21:17:56.416600Z

Usually you don't SELECT * on a join anyway because probably that data is going to a JSON response so you want to control what gets sent out and namespaced keys break down with JSON and random client code

seancorfield 2021-04-05T21:21:53.420200Z

We do not send qualified keys back to client code. But we do a LOT of queries internally that stay entirely inside our apps and the qualified names help avoid conflicts.

mauricio.szabo 2021-04-05T21:23:10.421400Z

To be honest, I was quite happy to use next.jdbc in a project... and to be honest, the experience was not that good. Most of the time I spent converting qualified keywords to JSON and back, and in the end I tried to make a builder-fn that would return the query exactly how @isak sent me. How did you do it? It would help me a lot on my code here 😄

wilkerlucio 2021-04-05T21:23:25.421500Z

not really, the idea (as I see) is about Entities, Attributes and Values.

wilkerlucio 2021-04-05T21:23:45.421900Z

with unqualified names, the context is given by something up (like a record, or a type, or something)

wilkerlucio 2021-04-05T21:24:10.422600Z

the idea with qualified names is that you can get rid of that "context" thing on the top, and consider that all attributes live in a flat world, like datomic and spec does

nilern 2021-04-05T21:24:30.422900Z

I have a hard time following code where the set of keys keeps changing although it is kind of idiomatic to be flexible with that

borkdude 2021-04-05T21:26:06.426400Z

afaik JSON has no problem with "foo/bar" keys

mauricio.szabo 2021-04-05T21:26:38.427800Z

@borkdude except that nobody uses it 😅

ghadi 2021-04-05T21:26:48.428500Z

A lot of Clojure programmers allocate way too much mental overhead by using different names for the same thing. This is one of the appealing and powerful attributes of spec. Global semantics + global names. I have seen a lot of CLJ + CLJS applications hurt themselves by using JSON on the wire.

isak 2021-04-05T21:26:49.428600Z

I think to interpret your data correctly, you almost always need to know 1) which view you are looking at, and 2) which path into that view you are looking at. And if you know both of those things, do you still need namedspaced keywords?

1
1
☝️ 1
borkdude 2021-04-05T21:26:55.428900Z

we use it at work in our (third party facing) APIs, we never had a complaint about it

isak 2021-04-05T21:27:50.429400Z

@mauricio.szabo Here is an example for SQL Server

declare @People as table (
    id int primary key,
    [name] nvarchar(255),
    dad_id int    
)

insert into @People([id], [name], dad_id)
values (1, 'Bob', null),
       (2, 'John', 1),
       (3, 'Jack', 2),
       (4, 'Jill', 2);

select a.*,
    JSON_QUERY((select * from @People b where a.dad_id = b.id for json path, without_array_wrapper)) as dad,
    (select * from @People b where a.id = b.dad_id for json path) as children
from @People a
where id = 2 /* John */
for json path
=&gt; [{:id 2, :name "John", :dad_id 1, 
     :dad {:id 1, :name "Bob"}, 
     :children [{:id 3, :name "Jack", :dad_id 2} {:id 4, :name "Jill", :dad_id 2}]}]

2021-04-05T21:28:39.429800Z

nah, that is the whole point to using fully qualified keywords

➕ 1
nilern 2021-04-05T21:28:47.430100Z

Overall the namespaced keywords hype smells of RDF and the Semantic Web, not a good smell. They solve some naming clashes etc. but not everybody is doing Data Lakes or whatever

2021-04-05T21:28:51.430200Z

the view and the path are both context

mauricio.szabo 2021-04-05T21:28:51.430400Z

Ah, right, I see. Yeah, that I can't use because I'm stuck with MySQL 😞

2021-04-05T21:29:11.430600Z

used to disambiguate ambiguous names

isak 2021-04-05T21:29:14.430800Z

Ah bummer. I know it also works in Postgres, but I don't think it works in MySQL yet, unfortunately

ghadi 2021-04-05T21:29:40.431300Z

lots of weak arguments in here

isak 2021-04-05T21:29:51.431600Z

Right, but it doesn't scale when you have more than one X per Y, you need the path in such cases anyway

2021-04-05T21:30:05.432100Z

when you have self joins

mauricio.szabo 2021-04-05T21:30:20.432500Z

Yes, I though you did by using some :builder-fn on next.jdbc

1
ghadi 2021-04-05T21:30:24.432900Z

the Semantic Web, while a failed effort, got it very right to use names that indicate shared meaning

2021-04-05T21:30:32.433200Z

and that depends to, but yeah definitely not as automatic

isak 2021-04-05T21:30:59.434300Z

Yea, or even just when you just have a book that was edited by one Person, and written by another Person

2021-04-05T21:31:07.434700Z

that is a self join

2021-04-05T21:31:17.434900Z

you are joining a person to a person

isak 2021-04-05T21:31:22.435100Z

No, book to person twice

2021-04-05T21:31:28.435400Z

nah

2021-04-05T21:31:33.435700Z

that is the same thing as a self join

wilkerlucio 2021-04-05T21:31:40.436Z

this is kind of the thing that Pathom tries to help with, once you have qualified (unambiguous) names, Pathom allows you to define how they relate to each other, and them you can ask for data by using pure inputs and outputs (I have this data, I want this data shape, go!)

👀 1
2021-04-05T21:31:55.436600Z

you join book and person once to get a record that is an amalgam of book and person

2021-04-05T21:32:01.436900Z

and then you join that to person again

2021-04-05T21:32:08.437200Z

it is a self join

2021-04-05T21:32:24.437800Z

the fact that the names conflict tells you it is

isak 2021-04-05T21:32:44.438500Z

self-join means the same table twice, no? Otherwise we would just say join

2021-04-05T21:33:38.439600Z

if you don't like self join, you can say "merging a person record into a record that another person record has already been merged into"

isak 2021-04-05T21:33:50.440Z

Example query:

select a.id as book_id, b.name as author_name, c.name as editor_name
from book a
left join person b on a.author_id = b.id
left join person c on a.editor_id = c.id

mauricio.szabo 2021-04-05T21:33:57.440400Z

I think my issue with namespaced keywords is that nobody (except Datomic and Datahike, maybe) use it. Once I tried to force myself to use it, and in the end I felt like Typed-OO again: I had a JSON or XML, had to convert to a map, then re-convert to qualified keywords, then convert back to non-qualified to persist in SQL... after some time I would have to query that DB, get non-qualified, re-qualify, work with it, de-qualify to send to an API...

2021-04-05T21:34:39.440500Z

person c is joined to the join of person b and book a

wilkerlucio 2021-04-05T21:34:44.440800Z

I have experience using namespaced keywords to handle large requests for complex front-end, in this case was a backoffice app that has to load dozens of widges about a customer (personal info, account info, transactions, etc...), and the namespaced keywords allows for a system where many teams add things on this system on daily bases, integrating multiple services, and worked very well (even though most people that end up working on this part of the system are from distributed teams, with no previous experience of doing things this wya)

2021-04-05T21:35:27.441300Z

so there already is a person in the join, so joining a person again is self join

isak 2021-04-05T21:35:28.441500Z

Ok, but it could have been written as subqueries, and since it they are left joins it would always give the same answer

wilkerlucio 2021-04-05T21:35:48.442500Z

what mattered in the end is that when devs are writing widget, the system allows them to just describe the data they need (using EQL), and not care about how its fetched. to add new names to the system people add resolvers to a single service, where all definitions are based on establishing relationships between the names

alexmiller 2021-04-05T21:35:54.442800Z

s/JSON/Transit s/DB/Datomic qualified names through the stack

2021-04-05T21:35:54.442900Z

having explicit translation layers between input data, storage, and app logic is good actually

wilkerlucio 2021-04-05T21:36:04.443Z

to me this is the experienced that convinced me that this approach scales big time

2021-04-05T21:36:31.443600Z

(forcing the db to hold OO logic etc. is not)

wilkerlucio 2021-04-05T21:37:25.444600Z

did you felt the same when using Pathom?

mauricio.szabo 2021-04-05T21:37:27.444800Z

If my client don't use Transit and my company don't use Datomic, then what? It's not that easy, specially Datomic being closed-source

2021-04-05T21:37:35.444900Z

when I say "join", while it does map cleanly to the database example with literal joins, I mean it abstractly as some operation that combines (joins) desperate records into new records

isak 2021-04-05T21:38:14.445900Z

ah, fair enough :thumbsup::skin-tone-2:

2021-04-05T21:38:19.446200Z

so I it doesn't matter to me if join appears in your query or not (heck, maybe the query planner will completely rewrite your query anyway)

1
alexmiller 2021-04-05T21:38:56.447200Z

my point is that there is a unified way of thinking here to create a stack that respects qualified names

alexmiller 2021-04-05T21:40:21.450900Z

we're trying to move the state of the art forward, not start from whatever broken substrate is the status quo

mauricio.szabo 2021-04-05T21:40:33.451300Z

Yes. But should an open-source language as pragmatic as Clojure be opinionated to use a thing that virtually don't exist outside that language? Specially when one of the pieces is closed-source?

alexmiller 2021-04-05T21:41:08.452500Z

have you not noticed that Clojure's author is opinionated? :)

isak 2021-04-05T21:41:09.452700Z

To be fair, it supports the other way very well also

2021-04-05T21:41:43.453800Z

trying to make the edges of your program look like its implementation is a pathology, that's how we got ORM, CORBA, etc - the data interchange format / storage do not need to look like the format your application uses internally

mauricio.szabo 2021-04-05T21:41:45.454100Z

Well, Pathom is an exception to the rule 😄

alexmiller 2021-04-05T21:42:20.455200Z

the goal is not similarity, it's solving the problem of disambiguating and describing data

nilern 2021-04-05T21:42:28.455400Z

But is building the whole stack around qualified names worth it just to avoid mixing up :book/name and :author/name? I would rather just use static typing, it catches so many more errors with less effort...

ghadi 2021-04-05T21:42:46.456Z

another strawman argument

2021-04-05T21:42:59.456700Z

static typing doesn't extend to the db or data interchange either

ghadi 2021-04-05T21:43:04.457Z

(disambiguation is not the only benefit)

nilern 2021-04-05T21:44:09.457700Z

No, but getting statically typed access to those requires work

alexmiller 2021-04-05T21:46:25.458700Z

"is building the whole stack around qualified names worth it" - yes, that seems to be the foundation of good naming in every system I'm aware of "so many more errors" - does it? (I seem to recall a lot of github issues on projects with statically typed languages) "with less effort" - is it? (that does not match my experience)

nilern 2021-04-05T21:48:21.459100Z

Idiomatic Haskell and ML is nothing like the C++ and Java typing travesties

mauricio.szabo 2021-04-05T21:50:47.459700Z

It's also open-source, and it's quite easy to just use "inside Pathom" and then have a resolver for a :json/payload that contains unqualified keywords on the format that you expect some API to use, so it's not really a big deal

marciol 2021-04-05T21:51:38.459900Z

I think that it's because Pathon gives us real examples of how leverage fqn's in documentation

mauricio.szabo 2021-04-05T21:53:32.460200Z

When I forced myself to use qualified all the way, I found out that a simple change (like, for example, adding a field into a payload) would propagate into multiple files, payloads, formats, converters, and after we had to make the 5th "+40 lines" change to add a single attribute, we ditched it into a simpler approach (that would be, coerce a schema, work with that format all the way until the end)

mauricio.szabo 2021-04-05T21:53:47.460400Z

BTW, I think Pathom didn't exist at the time 🙂

nilern 2021-04-05T21:54:33.460600Z

"If it compiles it just works" is a real thing (exaggerated of course). "Our map keys are context free" is pretty lame in comparison.

seancorfield 2021-04-05T21:58:56.461300Z

Saying “qualified keywords aren’t worth the effort because they don’t solve 100% of the problem” is missing the point.

alexmiller 2021-04-05T21:59:16.461800Z

it seems like you are comparing apples and staplers, I'm not sure what these things have to do with each other really

alexmiller 2021-04-05T21:59:30.462200Z

statically typed languages have just as much need for disambiguation of names

seancorfield 2021-04-05T21:59:41.462600Z

They add value wherever you need names to have a larger context than “just inside this one piece of data” — which means they add value in a pretty large space.

seancorfield 2021-04-05T22:00:26.463300Z

You can choose not to use them and justify it however you want, but you don’t have to work with me so we don’t need to argue about it 🙂

🙂 1
lilactown 2021-04-05T22:01:13.464500Z

namespaced kw's can be unambiguous across networks. if you're doing the same with ML you're basically using EDN

lilactown 2021-04-05T22:02:33.466600Z

likewise, if you're trying to check your usage of namespaced keys at compile time you're basically inventing a type system.

phronmophobic 2021-04-05T22:02:38.466900Z

It seems like people are interested in using qualified keywords, but finding good guides, resources, and examples is still a challenge. Hopefully, that will improve over time.

👍 1
seancorfield 2021-04-05T22:03:25.468300Z

Going in and out of the DB using next.jdbc means I can use qualified keys in both directions (`next.jdbc` ignores the qualification going into the DB, for convenience, and qualifies with the table name coming out — again for convenience), and you can explicitly map those names to whatever you need. clojure.set/rename-keys and/or select-keys means this is a “one-liner” at the boundary between naming — which I would expect at a boundary returning JSON since you’re unlikely to be exposing your internal domain data 1:1 anyway.

lilactown 2021-04-05T22:05:06.468700Z

thus having a system for disambiguating names isn't trying to be equivalent to a type system, and a type system isn't equivalent to having a notation for disambiguating names

seancorfield 2021-04-05T22:05:20.469200Z

I wish we’d used qualified keywords from day one, at work, to be honest.

lilactown 2021-04-05T22:05:27.469400Z

(trying to reframe alex's point)

2021-04-05T22:07:06.471Z

right - I think this points to the right focus - having good translation layers between input, logic, and storage, and namespacing of keys helps

emccue 2021-04-05T22:08:14.472400Z

If we rewound time and made it so that every json library for clojure didn't have an option to keywordize keys I think more codebases would be cognizant of it

1
seancorfield 2021-04-05T22:09:28.474800Z

I suspect if Clojure books and tutorials had used qualified keywords from day one, we wouldn’t be having this conversation 😉

marciol 2021-04-05T22:09:55.475800Z

exactly @seancorfield

seancorfield 2021-04-05T22:09:57.475900Z

@emccue Do you mean that JSON libs kept data structures with string keys?

emccue 2021-04-05T22:10:42.477100Z

yeah - unconditionally interning external input strings so that you can write (:key map) instead of (map "key") or (get map "key") blurs the line between "parsed data format" and "internal domain representation"

2021-04-05T22:11:15.478200Z

I may be reading it wrong, but I took it to mean that they "keywordize" option in the json lib leads to codebases without an explicit data transformation layer between input and app logic - the keywordizing is like a classic 80/20 of that

nilern 2021-04-05T22:11:39.478500Z

I was just comparing the cost/benefit ratio, not claiming that they are interchangeable

seancorfield 2021-04-05T22:12:27.480300Z

I can see the argument for drawing a starker line between JSON format and Clojure hash map format.

emccue 2021-04-05T22:13:09.481800Z

^not that its definitely a bad thing - but in the absence of educational materials that aren't either a rant on the clojure website or a rant published as a book how libraries/existing code works is probably the strongest driver of how people use a language

2021-04-05T22:13:13.482100Z

and namespaced keys is where that fallback really becomes a painpoint, since json doesn't have namespacing

emccue 2021-04-05T22:13:38.482700Z

(i guess and assert without evidence)

emccue 2021-04-05T22:15:09.485600Z

like, the better clojure books are "for the brave and true", which does a good job but it is just mechanics - how not what and why

lilactown 2021-04-05T22:15:48.487500Z

IME (I work mostly in distributed systems) fully qualified names has more benefit for me than static types. YMMV depending on domain

nilern 2021-04-05T22:15:54.487900Z

There was a time when "Cheshire and MongoDB, no more translation layers" was the attitude

emccue 2021-04-05T22:15:54.488Z

and the clojure for web development book - which isn't prescriptive about much beyond how to glue together the libraries in a way that works

2021-04-05T22:16:25.489100Z

not sure where that was the attitude?

lilactown 2021-04-05T22:16:31.489200Z

the problems that unambiguous names solve are more important to my domain than the problems that static typing solve

2021-04-05T22:16:34.489500Z

right, I've worked on apps that tried to do that and it works great until it doesn't work at all

2021-04-05T22:18:17.492800Z

I worked with the maintainer of cheshire at a clojure shop for a bit, and the general attitude there (not sure what his attitude was) was virulently anti-monodb

emccue 2021-04-05T22:19:08.494800Z

Thats because MongoDB is less a DB and more a long running bit thats going to end with someone taking a bow and shouting "The Aristocrats"

6
2021-04-05T22:19:30.495400Z

yeah, that

2021-04-05T22:19:50.495800Z

so there are lots of attitudes

2021-04-05T22:30:28.003Z

I get that "things that will exist in a global context should be qualified as such (ie full domain)" and "it can also be useful to qualify things categorically/by domain in our app" such as :user/email but I'm kinda lost at the idea of "json in clojure land should have used string keys by default". Does this imply that we should add qualifiers to everything at the boundaries of a program? Or is the point to keep keys as strings until making a decision about what qualifier a particular keyword should have?

2021-04-05T22:33:06.004200Z

I read it as an unfortunate side effect of a convenience - since keywordizing makes the incoming data look "native", people start structuring applications such that they lack the translation layer from the interchange format to the application

2021-04-05T22:33:19.004600Z

and that works until eg. you want to use namespaced keys inside the application

2021-04-05T22:33:43.005100Z

and you might get confused and think the solution is that the interchange format needs namespacing

marciol 2021-04-05T22:36:29.006400Z

I sympathize with those ideas, and I remember Joe also want to push the industry to a better way

💯 1
2021-04-05T22:37:41.008800Z

there definitely is a tendency to look at json as data structure and not a serialization format

2021-04-05T22:38:13.009600Z

really, the same with edn

2021-04-05T22:38:21.010Z

Lately I've been experimenting with, when getting a stream of a lot of different k/v pairs from a source, just namespacing everything :source/x as a reminder, but I'm not sure how good an idea this is, because a :source/type won't always have the same meaning

marciol 2021-04-05T22:39:31.011300Z

Right to the point @emccue, it'd be nice to have more resources about the Clojure way to solve real problems, at least, as the maintainers imagine should be this way, because the other way, we watch this fragmentation. Maybe, it's a tragic fate of Lisps.

marciol 2021-04-05T22:43:36.011400Z

An interesting approach, as it will denote the origin of data, and seems to be exactly what qualified keywords main objective, to tag data origin.

2021-04-05T22:48:07.012100Z

But on the other hand it doesn't really adhere to the spec philosophy, because a particular keyword will not correspond to a single semantic meaning

marciol 2021-04-05T22:49:04.012800Z

unless you carry this keyword across the system

alexmiller 2021-04-05T22:49:32.013800Z

I think there are a wide range of resources about this already

Ben Sless 2021-04-06T07:22:15.062100Z

One thing I find I'm missing here is the theoretical underpinnings, i.e. stuff from RDF land. I think it might help me get into the proper mindset. Are there any resources you can recommend on the subject?

alexmiller 2021-04-06T12:38:46.075400Z

https://www.amazon.com/dp/0123859654/ref=cm_sw_r_cp_awdb_imm_CS2VY2C913F4CYRQP4Z6 is a common entry point

☝️ 1
🙏 2
marciol 2021-04-06T14:06:35.087300Z

cc: @souenzzo

alexmiller 2021-04-06T14:09:56.087800Z

my own experience has been that the RDFS stuff is pretty useful, but OWL (which lets you do more reasoning) is challenging to use other than in a very constrained domain

🎯 1
Ben Sless 2021-04-06T14:10:26.088Z

Thank you 🙂

alexmiller 2021-04-06T14:11:55.088900Z

everything has to be set up "just right" and one set of contradictory statements can totally break your reasoner. the effort involved to set it up requires you to understand your domain so thoroughly that you could probably have written it in some other way that's not so brittle

alexmiller 2021-04-06T14:13:21.089300Z

there are really a handful of interesting ideas in RDF+RDFS - global identifiers (good idea, url impl super cumbersome), facts as EAV, the importance of A over E

alexmiller 2021-04-06T14:14:22.089500Z

we built some really great RDF libs at Revelytix in Clojure, afaik that IP never escaped

alexmiller 2021-04-06T14:17:17.090100Z

we had some cool things to make clojure-y views over rdf that made it actually tolerable to accommodate all the namespacing stuff

Ben Sless 2021-04-06T14:17:52.090300Z

unfortunately I don't think I'm familiar enough with RDF yet to appreciate this

alexmiller 2021-04-06T14:18:15.090600Z

also a federated SPARQL engine written in Clojure

marciol 2021-04-06T15:02:08.090800Z

it's an interesting domain, I still thinking about how to apply it conscientiously when dealing with the external world, which is generally messy

marciol 2021-04-06T15:03:06.091Z

my gut felling says that it's a great tool to use on these scenarios if you know how to leverage it

marciol 2021-04-06T15:04:22.091200Z

but I am still connecting the points

marciol 2021-04-06T15:06:38.091400Z

Thinking about a keynote or interview where Rich said that RDF can be used to join data across several databases, something that usually happens during acquisitions

marciol 2021-04-06T15:07:43.091600Z

This is a sort of messy real-world problem, and RDF seems to offer a good set of tools to deal with

marciol 2021-04-06T15:09:59.091900Z

But more interesting is the fact that these days, maybe, this kind of problem isn't so prevalent anymore? It's a random guess, given that my background is working on startups and small companies.

marciol 2021-04-06T15:13:33.092200Z

But @wilkerlucio Pathom is a really interesting way to apply RDF ideas in a set of problems that I'm facing right now: how to compose and merge information from several legacy systems in a healthy way.

2021-04-05T22:49:39.014Z

but they might have :source/type mean "event topic" in once spot and :source/type mean "car or truck" in another context

2021-04-05T22:50:00.014600Z

because to them "type" is just a local name and the context is required

marciol 2021-04-05T22:50:37.014900Z

Yes, it'd be nice to get an index. I made an index of articles, some from 2016, etc, but seems that they are all superficial

wilkerlucio 2021-04-05T22:50:52.015500Z

can you list a few?

alexmiller 2021-04-05T22:51:42.017400Z

As much as it’s possible to do, given that “real” systems are their own universes that live in the context of companies and groups of developers with histories, and thousands of micro decisions

marciol 2021-04-05T22:51:48.017500Z

yes @wilkerlucio, most talk about spec, that is a related subject

2021-04-05T22:52:01.017700Z

top two links are recent discussions, last one was an article that led to a few discussions (on reddit/clojureverse)

wilkerlucio 2021-04-05T22:52:35.017900Z

I mean, I understand we have these discussions on the topic, but I was trying to think of resources more like books, articles, and things that explain via example the idea of using qualified names from the beginning

wilkerlucio 2021-04-05T22:52:51.018100Z

because I guess for somebody looking to learn, just reading peoples experiences (in the middle of discussions) is not enough

➕ 3
☝️ 2
lilactown 2021-04-05T22:54:24.018600Z

provenance is one use of being unambiguous but IMO not the way it always should be used

lilactown 2021-04-05T22:57:24.019400Z

I typically use qualified keywords to denote the "domain" of the attribute. the source might be relevant or not

lilactown 2021-04-05T22:57:55.019600Z

e.g. I might load information about a user's account from three different sources; they are all about the same domain (a user) so they should all share a namespace

lilactown 2021-04-05T22:58:47.020200Z

this is very powerful because it means that my code is generalized across sources; it doesn't matter if I load it from cache, from disk or across a network, if I have an :myapp.account/id I treat it all the same

alexmiller 2021-04-05T23:00:31.020400Z

https://clojure.org/about/spec

alexmiller 2021-04-05T23:00:56.020600Z

https://corfield.org/blog/2019/09/13/using-spec/

alexmiller 2021-04-05T23:01:19.020900Z

https://www.youtube.com/watch?v=YR5WdGrpoug

alexmiller 2021-04-05T23:01:34.021200Z

https://www.youtube.com/watch?v=oyLBGkS5ICk

alexmiller 2021-04-05T23:02:53.021700Z

https://www.youtube.com/watch?v=nqY4nUMfus8&amp;list=PLZdCLR02grLrju9ntDh3RGPpWSWBvjwXg <-- playlist of screencasts we did about spec

alexmiller 2021-04-05T23:04:08.022Z

plenty of others from other people at https://www.youtube.com/user/ClojureTV/search?query=spec too

marciol 2021-04-05T23:04:58.022300Z

Seems that I get at least half of the important content @alexmiller 😅

alexmiller 2021-04-05T23:06:24.022700Z

well, that is in the past keynotes already

emccue 2021-04-05T23:06:26.022900Z

I don't think resources are the problem

marciol 2021-04-05T23:06:34.023200Z

But it's not easy to extract useful content of real work. Here, in the company I work we are doing very interesting things with @wilkerlucio Pathom, but we don't find time to write about how Pathom is helping us to solve complicated integration problems.

emccue 2021-04-05T23:06:46.023600Z

There are tons of resources for learning and putting clojure into practice

marciol 2021-04-05T23:06:50.023800Z

Nice

marciol 2021-04-05T23:07:09.024300Z

I see that you shared some. Thank you!

emccue 2021-04-05T23:07:11.024500Z

and a bunch of conference talks about what the intent is behind stuff

emccue 2021-04-05T23:08:02.025400Z

But there is no effective aggregation of that information

emccue 2021-04-05T23:08:22.026100Z

we don't have a "The Rust Programming language"

emccue 2021-04-05T23:08:35.026600Z

if that makes sense

alexmiller 2021-04-05T23:09:10.027100Z

"Programming Clojure" is as much that as anything

emccue 2021-04-05T23:11:13.028700Z

I don't mean in terms of a language tutorial

marciol 2021-04-05T23:11:16.029Z

wow @alexmiller, I have the 2nd Edition here

alexmiller 2021-04-05T23:11:29.029600Z

the 3rd covers spec and Clojure CLI

marciol 2021-04-05T23:11:35.030100Z

hmmmmm

emccue 2021-04-05T23:11:47.030800Z

In the rust community the language is a complicated beast

emccue 2021-04-05T23:11:55.031400Z

so thats basically all it covers

emccue 2021-04-05T23:12:24.033100Z

but I means in terms of a collaboratively built central resource

2021-04-05T23:12:26.033200Z

programming clojure is the only book about clojure on rich's clojure bookshelf(this is, of course, not fair because I think it was the only book about clojure when the bookshelf was created) https://www.amazon.com/ideas/amzn1.account.AFAABBRGIVOWVKTHP5NOJU5LMROQ/3BSKWCYM12RBZ

alexmiller 2021-04-05T23:13:28.034300Z

imo, there is no one answerable answer to this. everyone comes to Clojure with a different background, wants a different thing, is building different things. there are resources to answer almost any question you might have. it's impossible to organize them in all some way that makes sense to every person at every point in their path of knowledge. I've never seen that in any language community I've intersected with (other than communities so small that a single person can write a single definitive resource)

👍 2
1
alexmiller 2021-04-05T23:14:34.036600Z

in the last decade, there have been at least dozen, probably many more attempts to create such a thing. they are all helpful. they are all missing important topics. they are all useful to some extent.

marciol 2021-04-05T23:14:35.036700Z

Agree!

alexmiller 2021-04-05T23:16:14.038800Z

the Clojure web site is open for issues and PRs. I have helped people edit a variety of things into a published state there over the years and would be happy to help do more of that (as time allows). most of the guides https://clojure.org/guides/getting_started were contributed

alexmiller 2021-04-05T23:17:01.039600Z

I'd be happy to entertain organizational and expanding ideas in the community area

wilkerlucio 2021-04-05T23:17:44.041200Z

nice collection here :)

alexmiller 2021-04-05T23:17:46.041400Z

I actually did a big overhaul of the getting started area a couple months ago that is still pending a few things so has not yet been merged, but that's coming

👍 1
❤️ 2
alexmiller 2021-04-05T23:18:31.042800Z

oh, https://vimeo.com/195711510

alexmiller 2021-04-05T23:18:54.043200Z

I always forget that one

2021-04-05T23:19:45.043800Z

The last Clojurists Together survey indicated they might want to explore more creative things to fund... maybe "The Encyclopedia of Qualified Keywords: from Theory to Practice" could be funded into existence

💯 2
alexmiller 2021-04-05T23:20:18.043900Z

if someone wanted to bundle these links together nicely and add a PR for the end of the guide, that would be cool

marciol 2021-04-05T23:20:31.044100Z

Thank you @alexmiller This was my original question after all

marciol 2021-04-05T23:20:40.044300Z

I can do it

marciol 2021-04-05T23:30:17.047900Z

Nice @lilactown, I start to understand a little bit what you mean. So if you have a namespace with all functions that handle operations on users, you can denote user information on keywords from this namespace

marciol 2021-04-05T23:31:08.048400Z

So that you can know about the namespace from which a specific keyword originated

marciol 2021-04-05T23:33:50.049900Z

Interesting point!

lilactown 2021-04-05T23:35:57.051Z

that is one thing you can do yes

chicwayne 2021-04-05T23:36:18.051200Z

Thanks @marciol (and everyone who subsequently weighed in) for posting https://clojurians.slack.com/archives/C03S1KBA2/p1617654390371500 that led to such an prolific and informative thread, ending with https://clojurians.slack.com/archives/C03S1KBA2/p1617663102017400. It reinforces how important qualified keywords are. The term "tragically underutilized" jumped out at me the very first time I read the rationale.

🦾 1
marciol 2021-04-05T23:41:24.051800Z

You are welcome @dboggs85 I was talking about what @alexmiller said. About how people from different backgrounds can learn about some subject in different ways, and how it's important to take it into account. I'm already understanding, step by step, the importance of these concepts and I think that we need to make an effort to understand and communicate this understanding to others.

marciol 2021-04-05T23:43:11.052200Z

this is very similar to what I was accustomed to in Elixir, but in Clojure terms, what is much more powerful and somewhat strange 😅

the2bears 2021-04-05T23:44:41.052400Z

Great obscure reference (at least in a Clojure channel) gets this my early vote for "Comment of the Year" 😂

marciol 2021-04-05T23:46:45.052600Z

And about this @alexmiller? https://www.cognitect.com/cognicast/103