rewrite-clj

https://github.com/clj-commons/rewrite-clj
lread 2020-08-24T20:02:38.007500Z

Ok, I should figure out what I need to do for namespaced elements for rewrite-cljc. (It feels like I might have said this a year ago 🙂). Right now I am trying to guess if a correct sexpr on an auto-resolved namespaced keywords and maps would have real value to rewrite-cljc users. For reference, I am calling ::mykeyword1 current-namespaced and ::alias/mykeyword2 alias-namespaced.

lread 2020-08-24T20:04:33.008900Z

@vemv you had some https://github.com/lread/rewrite-cljc-playground/issues/7#issuecomment-667743070. I’m wondering if your issue has more to do with the awkwardness of working with the node than any issue specific issue with sexpr.

vemv 2020-08-25T09:22:35.073Z

I reviewed my usage of the technique I mentioned over GH I use sexpr, but as an intermediate step, that might as well be discarded with an alternative implementation. In the end I care about being able to replace ::foo/bar with ::quux/bar. In my codebase that is a Node to Node transformation, with some hacks in the middle. So, answering to your question (`if your issue has more to do with the awkwardness of working with the node than any issue specific issue with sexpr`), I think it's the former :)

lread 2020-08-25T16:51:19.073200Z

Thanks @vemv, I’ll take a look and see what I can do to improve in that area.

borkdude 2020-08-24T20:08:33.011900Z

I guess the most important things to consider are: 1) is such a node distinguished from other rewrite-cljc maps/keywords -- can you tell if it's current-namespaced, or alias-namespaced by looking at some property? btw. I don't think rewrite-cljc can determine properly if the a in ::a/foo is an alias or full namespace name 2) can it round-trip properly, yielding the same string

lread 2020-08-24T20:13:03.015Z

Thanks for the reply @borkdude. I was thinking of maybe adorning the node with a namespaced-type which would be :literal , :alias or :current.

lread 2020-08-24T20:13:35.015600Z

For ::a/foo you are saying a could be a namespace name or the namespace alias, right?

borkdude 2020-08-24T20:14:25.016300Z

I'm just trying this out and perhaps it's always an alias

borkdude 2020-08-24T20:14:35.016700Z

$ clj
Clojure 1.10.1
user=> (require '[clojure.string])
nil
user=> ::clojure.string/foo
Syntax error reading source at (REPL:3:0).
Invalid token: ::clojure.string/foo
user=> (require '[clojure.string :as str])
nil
user=> ::str/foo
:clojure.string/foo

borkdude 2020-08-24T20:14:43.016900Z

so in that case it would be a good name

borkdude 2020-08-24T20:15:34.018200Z

What is an example of a map with :literal namespaced-type?

lread 2020-08-24T20:17:05.019800Z

Oh that’s my terminology sorry, I did not find a single definitive terms so made some up. An example would be :my-prefix/mykeyword3.

borkdude 2020-08-24T20:19:45.021800Z

So the namespace and a boolean indicating if it's an alias

borkdude 2020-08-24T20:20:23.022400Z

:foo/bar -> foo, false ::foo/bar -> foo, true :bar -> nil, false

lread 2020-08-24T20:22:28.023900Z

I looked your version. It was helpful. Do you ever sexpr on, for example ::bar in clj-kondo?

borkdude 2020-08-24T20:22:45.024300Z

I guess I have some weird keyword for representing the current namespace: :__current-ns__

borkdude 2020-08-24T20:23:14.025Z

since symbols and keywords are separate namespaces you can do that

borkdude 2020-08-24T20:23:36.025500Z

let me try sexpr and see what happens

borkdude 2020-08-24T20:26:01.026300Z

user=> (p/parse-string "#:foo{:a 1}")
<namespaced-map: #:foo{:a1}>
user=> (p/parse-string "#::foo{:a 1}")
<namespaced-map: #::foo{:a1}>

borkdude 2020-08-24T20:26:45.027200Z

I guess sexpr is not a valid operation on these nodes since it will depend on your aliases

lread 2020-08-24T20:26:50.027400Z

For your 2nd point on round-tripping I think that won’t be challenging and is not an sexpr concern.

borkdude 2020-08-24T20:26:51.027500Z

in your runtime

borkdude 2020-08-24T20:27:29.028400Z

I solved this problem in edamame by allowing the user to provide a function to resolve the alias

borkdude 2020-08-24T20:28:04.029600Z

since you won't break the API of rewrite-clj, you could consider a dynamic var for the same purpose, or extra args in sexpr

borkdude 2020-08-24T20:29:04.030800Z

it's only problematic with aliases. I think blindly resolving it to the runtime's aliases is not good

lread 2020-08-24T20:29:11.031100Z

Yeah… rewrite-clj current has some partial support in for alias-namespaced maps where it does a (ns-aliases *ns*)…

lread 2020-08-24T20:29:27.031600Z

Which implies that the code being parsed has been loaded.

borkdude 2020-08-24T20:30:24.032800Z

which also implies a coupling between the tool parsing the code and the code being parsed, which does not make sense at all.

lread 2020-08-24T20:31:09.033600Z

I’m not a big fan of the current approach.

lread 2020-08-24T20:31:52.034400Z

I noticed that cljfmt actually parses the ns declaration to get aliases and considered that as an option.

borkdude 2020-08-24T20:33:02.035400Z

I think that logic belongs somewhere else

borkdude 2020-08-24T20:33:30.036Z

you should just be able to parse #::foo{:a 1} with rewrite-cljc and provide an option how to resolve the aliases

borkdude 2020-08-24T20:35:34.038600Z

before you know it you're writing a clojure interpreter. consider:

(ns foo (:require [clojure.string :as str]))
(in-ns 'bar)
#::str{:a 1}

lread 2020-08-24T20:35:47.039Z

I was thinking that rewrite-cljc might provide a rudimentary parser/resolver, but you could also plug in something else. But you are thinking such a thing does not belong in rewrite-cljc?

borkdude 2020-08-24T20:35:49.039300Z

I can probably come up with a number of other examples where this will break down

lread 2020-08-24T20:36:00.039500Z

Yes, that’s the rabbit hole I am struggling with.

borkdude 2020-08-24T20:36:36.041Z

I would do it in the reverse: start with the pluggable option, don't go down the rabbit hole until people actually want this to be part of the library

lread 2020-08-24T20:37:20.041600Z

Yeah, that’s good advice.

borkdude 2020-08-24T20:38:37.043600Z

The option before that could be: don't support sexpr for these nodes.

borkdude 2020-08-24T20:39:02.044200Z

Can people implement sexpr themselves via some protocol?

borkdude 2020-08-24T20:40:49.046600Z

(sexpr nsm {:resolve {'str 'clojure.string}})

lread 2020-08-24T20:40:50.046800Z

Yeah, that was one consideration, but I think that would break some rewrite-clj fns. I think I’ll have to return a technically incorrect value for sexpr for auto-resolved namespace elements. This result could be improved by a plugin.

borkdude 2020-08-24T20:41:58.047600Z

In edamame:

(parse-string "[::foo ::str/foo]" {:auto-resolve '{:current user str clojure.string}})
;;=> [:user/foo :clojure.string/foo]

borkdude 2020-08-24T20:42:31.048100Z

I'm using this in sci where I already know the aliases because of handling the previous forms

lread 2020-08-24T20:43:11.049Z

Yes, at a minimum I’ll provide some mechanism for people to provide current namespace and aliases via hand-coding…. if that’s what you mean.

borkdude 2020-08-24T20:43:23.049400Z

hence where I'm getting the idea from that an interpreter and a parser/rewrite-tool are separate things

borkdude 2020-08-24T20:43:40.049900Z

yes

lread 2020-08-24T20:43:51.050300Z

I’ve always been half-annoyed that rewrite-clj includes sexpr at all.

borkdude 2020-08-24T20:44:38.051500Z

well, it's convenient, but I think in 99% of cases I call it to determine the type of thing

lread 2020-08-24T20:44:44.051800Z

I can see that it makes usage more convenient though… but in some cases, like this one, well.. it is not..

lread 2020-08-24T20:44:49.052100Z

what you said!

borkdude 2020-08-24T20:45:11.052600Z

so it would have been cool if rewrite-clj supported something like (type nmp) -> :namespaced-map

lread 2020-08-24T20:46:05.053500Z

There’s (tag nmp) -> :namespaced-map

lread 2020-08-24T20:46:28.053800Z

But tag granularity is not likely what you might expect.

borkdude 2020-08-24T20:47:55.054100Z

yeah, tag often returns token which can be a lot of things

lread 2020-08-24T20:48:01.054300Z

Exactly

lread 2020-08-24T20:49:09.055200Z

Anyway, I greatly appreciate the chat. I’ve been stuck in a bit of analysis-paralysis and I think you thawed me out. :simple_smile:

borkdude 2020-08-24T20:50:15.056300Z

:)

lread 2020-08-24T20:51:01.057100Z

Is adding (type node) worthy of a git issue? I haven’t given it any real thought…

lread 2020-08-24T20:52:13.057300Z

Oh maybe it is similar to: https://github.com/lread/rewrite-cljc-playground/issues/5

borkdude 2020-08-24T20:52:42.057700Z

I think so yes. It would save me writing code like this: https://github.com/borkdude/clj-kondo/blob/2fb61754456e6d35f136b340e8aa40064699c907/src/clj_kondo/impl/types.clj#L213

lread 2020-08-24T20:53:50.058300Z

Ok will update the issue.

borkdude 2020-08-24T20:59:29.058700Z

It doesn't have to be in the initial release I think, since it's not part of the original

lread 2020-08-24T21:01:28.060400Z

Agreed! The only reason it would make it into the initial alpha release is if I found a real need to procrastinate on a must-have alpha feature/fix :simple_smile:.

borkdude 2020-08-24T21:09:42.060800Z

I think parcera doesn't have sexpr right?

lread 2020-08-24T21:23:55.061600Z

I haven’t dug deep into parcera, but I think you are right. Parcera seems simpler in many ways.

2020-08-24T22:07:49.061900Z

i am not sure about the no sexpr bit -- i don't remember well enough what sexpr does atm (just woke up). but parcera does have quite decent successful round-tripping afaict (have tested against large number of samples).

2020-08-24T22:08:29.062900Z

btw, wrt terminology (naming of constructs), i think there might be some benefit in examining what various grammars are doing. if nothing else it might make talking about some things easier.

2020-08-24T22:11:34.065500Z

for example, it may just be my ignorance, but in the parcera grammar i encountered the term "macro keyword": https://github.com/carocad/parcera/blob/master/src/Clojure.g4#L157 i might have mentioned this before, but i have a version of the tree-sitter grammar that tries to merge some ideas from parcera and the naming is part of that. i don't know if anyone is up to making a glossary of terms, but in the matter of grammars, it seems like it could help reduce confusion (e.g. some people say "discard" while others say "ignore"). i also started saying "metadatee" to refer to the thing the metadata is supposed to apply to.

borkdude 2020-08-24T22:15:46.066500Z

@sogaiu sexpr transforms a parsed node into a real clojure thing

2020-08-24T22:18:00.066800Z

ah thanks -- i don't think parcera does that.

lread 2020-08-24T22:38:43.067600Z

In the other direction rewrite-clj also supports coercion form real clojure things to rewrite-clj nodes.

lread 2020-08-24T22:41:05.069800Z

For terms I’m kind of stuck with what I’ve got for rewrite-cljc. But where I have wiggle room, for Clojure elements, I am going to try to use what the Clojure core team uses. For example, I just asked on #clojure about namespaced element naming and got a response form Alex Miller.

2020-08-24T23:36:41.071400Z

cool you got a response! i think it totally makes sense to use what the core team uses -- but afaict, they don't have terms for everything.