{:start #time/local-time [2 0]
:every #time/duration {:hours 24}}
This is the same format I used in edn config files so it looked natural to also use it in code. Some data_readers and REPL trickery later and everything works out ok, except when it doesn't. I eventually gave up debugging why it sometimes doesn't work but to be honest reader literals are a bad idea to use in code. It became very obvious that there was also no point to using them in the first place. If you use actual code (which also sets up :require correctly, not hidden somewhere) things looks almost identical and some issues go away completely since those things are now constructed when the code is EXECUTED not when it is READ.
{:start (time/local-time 2 0)
:every (time/duration {:hours 24})}
Anyways, that is my experience and I have since completely stopped using tagged literals in code. To be honest I'd call data_readers.clj a mistake but YMMV.
Just my 2 cents,
/thomas
I should note that I actually used data literals in my code pretty much since 1.4 and it all worked as expected once I had everything configured. I recently reorganized my projects a bit and somehow messed up the config for the reader stuff which made it not work anymore.
I just didn't feel like trying to get that working again and settled on rewriting the literals I used to instead use actual code.
The problem with tagged literals in source as I see it is the complexity they introduce for very little gain. (none actually but that is my bias)
Take my example: I basically just grabbed the #time/duration literal and called it "mine". What happens when clj-time (or other libraries in that domain) suddenly wants to do the same?
Sure we could use fully qualified names but since we do not have the aliasing facilities the ns macro provides that would probably mean more typing.
Also since the namespace I use the tagged literal in does not declare that it depends on that functionality we end up with something hidden in our dependency tree which is a problem in itself.
Your suggestion in the gist of moving the data_readers file into a directory that corresponds does not solve the conflict problem, it is just somewhere else now.
As awesome as that experience was, I honestly would not want someone to be able to change what my code means just because the user decided to mess with some data literal options.
You are introducing new data types to the _compiler_ it does not know or understand.
Not sure how much of an issue that is in Clojure but I know that this would be a big issue in CLJS.
Having spent quite a bit a time in the CLJS compiler internals I can say that I definitely would not want that complexity there. As a "tool" author I can definitely say that I do not want anything that makes handling code more complex.While my opinion is clearly biased I would not consider it to be so because something negative happened. I honestly do not see anything that even a "perfect" integration would provide? What do you gain (or hope to) with regards to tagged literals in code?
I'm totally for improving the situation but you'll first need to convince me that there is an actual problem this solves.
Can you provide an example (code) where any of what you are proposing is illustrated?
The tool I was referring to is shadow-build [1] which is a build library for CLJS. It interfaces very directly with the CLJS reader, analyzer and compiler and does some things very differently than cljs.closure. Integrating support for "custom" tagged literals, even on a per ns basis, is actually not hard. Getting the compiler to recognize them however is very hard. Reading&analysis happens in Clojure, the compiler then emits javascript.
Say we now try to compile #time/duration, which results in a java.time.Duration. This class does not exist in Javascript and you'd somehow need to teach the analyzer&compiler how to construct something else in its place. If you'd just use (time/duration ...) in your code, the compiler does not need to know anything at all. It just creates a normal function call and the rest happens at runtime.
I'm sorry, I think you're misunderstanding my intentions. I'm looking for constructive discussion, not for people to convince.
I'm not proposing anything yet, so I don't have any example code. I'm still struggling with a coherent definition of the various problems we have with configuring the source. Will you help me find such a definition?
Good point there! The compiler can't assume that the runtime will use the same representation as itself, especially during cross compilation. That's an important design constraint.This also means that my previous statement about the requirement of being serializable, is wrong. The compiler can't directly serialize literals, but it has to work from the semantics of embedding #time/duration "42ms" as `(binding [*data-readers* ~target-source-readers] (read-string "#time/duration\"42ms\""))
Say we now try to compile #time/duration, which results in a java.time.Duration. This class does not exist in Javascript and you'd somehow need to teach the analyzer&compiler how to construct something else in its place. If you'd just use (time/duration ...) in your code, the compiler does not need to know anything at all. It just creates a normal function call and the rest happens at runtime.Well, the compiler would know that java.time.Duration *prints* as #time/duration "...", and therefore it can know how reconstruct the value at runtime. That implies that the compiler needs two matching sets of reader-tags, when compiling AOT/CLJS: One for the source code representation (passed to macros and such) and one for the target representation. Do you agree that this would solve the issue?
Isn't it the point of any good discussion to convince other people that their point of view might not be "correct"?
I don't see any issues with configuring the source at all, except for data_readers which I think should be removed as I said before.
The compiler can't directly serialize literals, but it has to work from the semantics of embedding #time/duration "42ms" as `(binding [*data-readers* ~target-source-readers] (read-string "#time/duration\"42ms\""))That would be problematic with advanced optimizations and would also have horrible runtime performance since you are basically parsing this over and over and over again if you use a literal in a function body.
That implies that the compiler needs two matching sets of reader-tags, when compiling AOT/CLJS: One for the source code representation (passed to macros and such) and one for the target representation. Do you agree that this would solve the issue?This leaves way too many things for the compiler to worry about instead of letting the user just do it in code. Configuring a CLJS build is already way too complex, letting the user work this out would not be exactly user-friendly. I prefer to work with code rather than configuring a tool, which is also why using shadow-build is just writing clojure and not a config file. ;)
Now to get it off my chest, I'll not answer to the rest of your email linewise, because AGAIN, I don't want to discuss whether reader tags, reader conditionals or any other feature already in clojure "are a good idea". I can only point you to the previous discussion that went into designing those features.
clojure.core/read-string and clojure.edn/read-string currently share a lot of implementation details but already are considerably different.
clojure.core/read-string (which under the hood uses the same reader clojure uses to parse source code) supports read-cond, tagged-literals, metadata, #=(eval calls) via *read-eval*, #my.Record. {constructor calls} and probably more.clojure.edn/read-string only supports tagged-literals.
I'm advocating removing tagged-literals from clojure.core/read-string. Which is currently the only place data_readers is used, so their is no need to configure them in the first place.
With removing tagged-literals from clojure.core/read-string the need to configure the compiler and build tools completely goes away, which is arguably the simplest solution.
I'm trying the understand your opinion on tagged-literals in clojure.core/read-string as you clearly see value in them, which I do not.
Please beware the distinction between the 2 readers I make here. One reads code, one reads data.
clojure.core/read-string should never be used to read data,
which is why clojure.edn was created in the first place.
I apologize if we are talking about different things or I did not make my point clear enough earlier.
OK, now that statement would be laughable in any programming language. For a lisp, it crosses over into the firmly absurd.Newsflash: CODE IS DATA AND DATA IS CODE
- How would you go about removing data_readers? (You partially answered this, but not the sub-questions)- Would you leave in the current default reader tags for #date, #uuid, ...?- If so, why only those?
- Would you, on the same grounds, also reject support for configuring custom read-cond flags?
- Would it be an error then, to return data, read by edn/read-string, from a macro, or passing it to eval by any other means?
--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at http://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.
Can we back this discussion up to the beginning and describe an actual problem we are trying to fix?
One specific issue mentioned was namespacing ("I grabbed time/..." but this could conflict) - the answer to this is to use your own less-general namespace. (I think grabbing "time" as a namespace is a bit presumptuous. Good thing you didn't also grab "space". :)
All data_readers.clj on the classpath are read and combined.
However, the general recommendation in the past is that libraries should not include them and should instead define how to install tagged literal readers in a single application level map.
Can we please re-orient it to describing a specific problem with tagged literals etc as they exist now?
Sure, the problem to me is, that when (require '[dependency.foo]) crosses over a project boundary into another source dependency, clojure currently provides no mechanism to account for a different reader configuration there.
Currently possible workarounds would be:
The generally correct answer, to grab a namespace that you DNS-own (or at least own on clojars ;-), is not always applicable or desirable. So, thinking about isolating source is the right mindset to also think about explicitly renaming tags, or shorthanding them. A system like clojure's shorthand - keywords ::sh/kw, based on namespace aliases, might be a good fit.All data_readers.clj on the classpath are read and combined.Ah, I wasn't aware of that. I thought that it was a first-one-in wins, as with other classpath resources.
However, the general recommendation in the past is that libraries should not include them and should instead define how to install tagged literal readers in a single application level map.In effect this prevents a library to make use of tagged literals within its own source, if it wants to do source distribution. That is, because the user might choose to include different data_readers or none at all.
For read-cond, the situation is similar. I think the decision to only allow single keywords as features is good, but a project needs to be able to derive its own features.
Projects need to be able to express such configuration in a build-tool - agnostic way, so that we can guarantee consistent results on repl and aot.
Given two programs to combine, both utilizing a conflicting #time/duration tag, I'd like clojure to support any of the following resolutions:1. You actually want to override reader-tag definitions in the source programsa. the two definitions actually construct the same type, so you can make a reader tag fn, that accepts a superset of both their input syntaxes.b. the constructed types can be made interchangeable via protocols2. data-flows going through either library are held separate, conversion is done explicitely3. definitions don't actually conflict, but the user decides to refer to the reader-tag by a different name
Ideally, there would be some means of forming a closure of reader-config over some source code, serializing the result to a jar-file or class-folder, and have everything be robust against composition, yet overridable.
Were it not for the "overridable" part, the answer might be as simple as AOT-compiling or transpiling, but since overriding is part of the intention, and since our evaluator directly understands reader-config, it should also understand a way to read non-uniformly configured code.