-
Notifications
You must be signed in to change notification settings - Fork 32
The (.) macro's design is broken #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The computed member expression macro is called
It's mentioned toward the end of the tutorial section about arrays and objects. I realise the distinction can be confusing though, and the docs should communicate it better. |
In JavaScript, |
Ah, kk. I disagree with the design, but at least it can be worked around.
The difference is that in The Similarly, if I assume that It's common in Lisp APIs to use self-evaluating symbols ( (Another inconsistency in the current approach - |
Thanks so much for your thoughtful analysis. I think I see what you mean now. What you suggest is that if the Test cases:
I think it's a great idea. I could implement it tomorrow. Can I ping you for a review? Eslisp checks the final AST being compiled to JS for illegal identifier names (e.g. If you prefer to use dash-separated variables, eslisp-camelify can automatically turn them into camelCase. This is pretty convenient with the |
Yeah, I'm happy to do a review. ^_^ |
Fixes #13. As pointed out by @wtabatkins in <#13 (comment)> , the difference between the `.` and `get` macros has been confusing: The list `(. a b)` has previously compiled to `a.b;`. This required a separate form `(get a b)` to produce `a[b];`. With these changes, `(. a b)` compiles to `a[b];`, and `a.b;` is produced by `(. a "b")`. This lets us discard the `get` macro, so the `.` macro corresponds to a member expression with full generality. This keeps the language core minimal and easier to reason about. This change does make the common use-case of long member expression chains awkward to type if using core eslisp. For example, what used to be `(. Array prototype slice)` will need quotes around the properties: `(. Array "prototype" "slice" "call")`. However, if that is your use-case, you should already be using macros (e.g. [eslisp-propertify][1]) to sugar it. If you preferred the old behaviour, you can still have it by writing the appropriate macros. [1]: https://github.com/anko/eslisp-propertify
Fixes #13. As pointed out by @wtabatkins in <#13 (comment)> , the difference between the `.` and `get` macros has been confusing: The list `(. a b)` has previously compiled to `a.b;`. This required a separate form `(get a b)` to produce `a[b];`. With these changes, `(. a b)` compiles to `a[b];`, and `a.b;` is produced by `(. a "b")`. This lets us discard the `get` macro, so the `.` macro corresponds to a member expression with full generality. This keeps the language core minimal and easier to reason about. This change does make the common use-case of long member expression chains awkward to type if using core eslisp. For example, what used to be `(. Array prototype slice)` will need quotes around the properties: `(. Array "prototype" "slice" "call")`. However, if that is your use-case, you should already be using macros (e.g. [eslisp-propertify][1]) to sugar it. If you preferred the old behaviour, you can still have it by writing the appropriate macros. [1]: https://github.com/anko/eslisp-propertify
Why not use |
@Gonzih I don't understand. Could you give an example? |
It might be a bit far from current implementation and ideas. Also might be influenced by clojure heavily. What I don't like is that property access and function invocation are separate things. It's to verbose to get function and then invoke it. For me it feels much more stricts to use get to access properties, but (. a b c) would alsay compile to a.b(c). But when I think about it it might be different from original design :) |
My idea is to keep the core language very plain (so e.g.
|
ok, I understand. I will try to play with user defined macros for my needs. Thanks! |
Then I would say that removing |
Ugh i forgot about names that collide with things like catch/for etc. If it's only case is it possible to make . macro a bit smarter and if name collides with reserved keyword convent this call in to ["for"]?
of course then you kind of tie macro implementation to the underlying js implementation without any easy way to extend it. Removing get feels like removing a bit of possible flexibility (have no idea if this flexibility is actually needed though). |
@Gonzih The flexibility is sometimes needed. Quoting @lhorie from chat:
All three of With that, I'm closing this and #14 (my implementation PR) as not an issue. Many thanks to all involved in discussing this, especially to @tabatkins for raising it and taking the time to explain so thoroughly, @lhorie for the above insights and @whacked for an overview of analogous syntax in wisp and cljs. |
No we don't, and the current design already conflates them heavily anyway; the distinction is artificial and confusing. I described in the PR what needed to be done to maximize use of the In general, the only place where a lisp macro treats a symbol literally (rather than assuming it's a variable/function) is when it's a definition macro, like (Symbols-as-strings did happen in very old Lisp, long before Common Lisp and the string type, but that's so old to be irrelevant to our discussion.) |
@tabatkins In that case, I must have completely misunderstood. To be maximally clear, what forms do you think should compile to |
You might want to accept |
@tabatkins which means
one problem with not having an strict compile to bracket-access syntax is if you use a minifier, 2 is subject to renaming, but generally if you specified the string-quoted attribute already, you don't want it to be renamed [1]. In effect the dot accessor here acts like [1] http://squirrel.pl/blog/2013/03/28/two-ways-to-access-properties-in-clojurescript/ |
That's the problematic part. For this reason—
—there needs to be a way to write both the With your idea, for a given b for which both the |
Ah, that's an interesting objection. In that case, how about everything always desugars to brackets, except self-evaluating symbols ( That gives us complete consistency, while allowing the minifiable |
@tabatkins to my knowledge the current state is to use I haven't seen any discussion of the |
That's correct, with the small detail that As for |
@anko re: |
JS has "just strings and identifiers" because it has syntax that lets it provide special rules for how to interpret them in different circumstances; that's how it can get away with Lisp has a vastly simplified syntax, and so it has to be somewhat more explicit about a number of things. Or it can "fake" syntax, like cljs (validly) does, with specially-constructed names like Ultimately I won't fight this too hard; if it's a general design philosophy to use identifiers like this, then ok, it just means I'm definitely not using the language. ^_^ I need to be able to predict how the language works, and when ordinary functions start invoking unusual parsing rules that need to be memorized, I'm out. |
For posterity, I had suggested this in the chat: Re: |
I'm really warming up to @lhorie's
Is there anything bad about it? |
personal vote 👍 caveat is my experience is only from cljs and wisp, but within these families this change looks not bad, and I will use this feature |
Quote is fine, yeah. In "real lisp" it causes the value to be of type SYMBOL, same as And yes, the object notation from #23 has the same problems. I was going to bring it up when this thread was resolved. ^_^ |
Reopening to clarify that the proposed solution hasn't yet been implemented. |
Suggestion for this, inspired by my suggestion in #23: make static properties |
@IMPinball Yep, that's the plan; @lhorie outlined it similarly before too. The issue blocking me from doing this right away is that currently I'll hopefully soon have more time and attention to focus on it. |
@anko I think the difficulty is that sexpr-plus aliases |
My tentative implementation is coming in a PR I'm currently working on, which will fix both this and #23. |
i'd expect |
You want |
@vendethiel right, |
You might be interested in https://www.npmjs.com/package/eslisp-chain |
@vendethiel thanks |
In the README, you imply that
(. a 3)
compiles toa[3]
(good), but then state that(. a b)
compiles toa.b
(in other words, for all but the first argument atoms are converted to symbols/strings). This means it's impossible to access into a data structure with a variable; there's no way to write the equivalent of JS'sa[b]
.To do this properly, named property access needs to always be done with strings, like
(. a 'b')
, or at least with self-evaluating symbols, like(. a :b)
. This way everything that looks like variables is actually variables.The text was updated successfully, but these errors were encountered: