-
Notifications
You must be signed in to change notification settings - Fork 1
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
Variable / property namespacing #12
Comments
By the way, a way to think about property / variable accessing is basically, this:
is sugar for this:
(Unless we want to get clever with how we select which var values are valid) |
There's one more important thing I guess we forgot to discuss earlier.
At quick glance it's not that difficult to target this for var fields using "indirect variable referencing" syntax as the base, e.g.
but then:
So we need more ideas. |
@seven-phases-max Are you just talking about:
If we do Also, I guess that could include variations of |
Yes, but then should not we also require
|
Now, after thinking of it a bit more,
Do we really need to be that strict there at all?
Sure it's about "guessing a user intention, bla-bla-bla" - but can we imagine any practical snippet where you have: #ns() {
@foo: 2px;
foo: @foo * 3;
} and really want to have an access either to |
And yet another problem I did not want to mention before because it's also quite a story on its own. Let's start with this snippet:
Now let's try some basic overriding:
^ works like a charm. Now something more close to practical use-cases:
Fail... :( ( That's something we'll have to target too. (Note that we cannot change mixin evaluation behaviour in this case - reason). |
At first I didn't know what you meant until I read it. And then it makes sense and yes, does increase the simplicity of the syntax.
I think that's a smart observation, that in most cases, it won't be ambiguous for the user which type of value they wish to return, as long as we document precedence rules.
I think what you mean is the evaluation order, no? I don't think it's intended to be "local variables win" (and I hope it's not documented that way) so much as mixins are evaluated and then merged. So a user to expect otherwise is to not know that mixins are evaluated before returning (even though that's consistent with other mixin behavior?) So, yes, namespaces are not open to each other. However, Less already does this kind of "referential collapsing". That is:
The contents are not "open to each other". One reference is given, but both are called, evaluated, and returned. So, no, there's not one namespace. Whenever you referencing a "namespace" you're calling a collection. The same would be true with properties and variables.
The second
So it's just straightforward Less evaluation. It's evaluating the mixin, then returning the variable. So this:
is effectively
Make sense? Nothing new is changing in behavior, just a more convenient syntax. You're collapsing 4 lines into one. |
That's what I mean, if you change the example to
it's not anymore equal to
I.e. for |
Oh, ok, that. But..... if someone is doing that, I'm just trying to think why we would need to address that specific case. I don't think we need to try to solve all of Less's weird scope edge cases just to make the syntax more convenient for 99% of use cases. |
No, I actually did mean:
Then answer to:
would be the question "where did we specify that (OK, docs) and does anybody read that there?" :)
thus they will expect
to do the same (and then we'll have "Less is not JS, bla-bla-bla, RTFM"). But yes, if we won't agree on that my (quite radical, yep) variant, then:
is a good option. |
It's not quite the same as JS, because we're using unquoted identifiers. When we write this:
...the equivalent in JS would be:
It's just that CSS/Less allows plain keywords, so quoting here is not consistent. |
It's again only because you know what it's supposed to be. But they know only that JS |
Back to evaluation:
Actually it is one of the primary use-cases for the namespaces. // .....................................................
// framework:
#theme {
@color: red;
@background-color: #fff - @color;
@secondary-color: lighten(@color, 25%);
@alt-color: spin(@color, 45);
// etc. etc.
}
.btn {
color: #theme[color];
background-color: #theme[background-color];
}
// .....................................................
// user app:
#theme {
@color: blue;
// here I expect all depended theme colors to change accordingly
// (just like it *works* if I'd use global vars)
// "Your namespaces are useless, guys! I'll stick to my global garbage" <- that's why it's important
} P.S. There're workarounds as usual, but they are... well... workarounds (like this). |
Alright, that's possible lol. So, do you have a proposed solution? |
Maybe. There are a lot of people who know Less/CSS who aren't JavaScript developers. But.... I think your more important valid point is that people will probably want the identifier to be variable. Which I see is why you you suggested retrieving prop or var without Less has historically often chosen simplicity over complete non-ambiguity, such as auto-casting classes / ids as mixins. So ambiguity is not necessarily a failure, as long as the result is mostly intuitive and expected and can be clearly documented. |
For namespaces? The implementation side is trivial (just like we've noticed above it's "collapse then evaluate" instead of "evaluate then collapse"). The only question is how consistent this will look (as |
Good point. It would be nice if we could get more opinions (at least just to make sure we don't miss some critical pitfall in this case). |
I'm not understanding the namespace idea, but here's what I see as being useful: @var { // similar to @keyframe
key: value
}
element {
property: @{var.key};
// OR
property: @{var, key};
} |
It's exactly the same thing... In general, "namespace" can be considered as a synonym for a map-like-object too. |
@stevenvachon What @seven-phases-max said. Essentially it's treating a ruleset similar to a collection of key / value pairs. The only difference would be syntax. @var: {
key: value
}; // currently requires semi-colon after final brace
element {
property: @var[key];
} The reason it wouldn't be "dot notation" is because you can already chain namespaces like:
|
Just saw that this discussion was moved here, so I'm adding my 2¢ because of the invitation there. I super love where this is going.
Simple, intuitive, and unambiguous. I wonder, though, if the
Although I guess that could be resolved as Less usually resolves conflicts: just go with whichever it matches last (i.e. |
I'd rather suggest
Btw., it's important to pre-alarm that while properties become available as variables because of #2433 and (in the "namespacing" context) possibly less verbose code (by skipping |
I can dig the "use variables for variables" deal, but I do think there's a need for private variables (not as a replacement for standard variables, but as a supplement, allowing for clarity between theming variables and internal-use variables in a framework). I think As for conflict resolution, I don't think it's a super likely case anyway, so I think either prop > var or vice-versa would be just fine. My first instinct would be var > prop, but I honestly can't really say why, so I'm not too fussed either way. |
The reasoning was that #ns[ident] was a bit cleaner for referencing properties, and actually matches the original syntax. But then @seven-phases-max had the idea to allow it to reference vars or props, which allows some flexibility if the author writes their private definitions as either. And then, for explicit needs, @ and $ exist. That said, I actually think writing #ns[ident] to reference an Of course, we could , as I think was discussed in this thread (in a meeting so didn't have time to review all of it), document referencing as "vars: #ns[@Ident], props: #ns[ident]" and still allow #ns[ident] to fallback to a variable lookup if a property isn't found. However, I'm not sure: would that make troubleshooting a more difficult? Theoretically, the number of properties / variables in a given rule should not be massive, so the variable fallback seems okay. And, if they have a property / var with the same name....
Basically this. |
In a framework like Bootstrap (which has 375 variables in v3.3.6) the authors may not namespace groups of variables within their framework's main namespace, so I don't think we can be sure that having loads of props/vars in a namespace is necessarily gonna be all that uncommon. But it does make more sense to let the developer be responsible for keeping his/her own code unambiguous.
I think, if behavior is intentionally included, it should be documented. Whether strictness or flexibility is the higher value, the docs should inform developers what they should expect from their code. For example: "You can access a variable or property with TL;DR Whatever the behavior is in the end, as long as it's clearly and fully documented, I don't think people will run into too many issues. |
Those are a good thoughts. There was a lot of discussion to get it to On this:
In the property accessor implementation, it's pretty similar to vars. Nearly identical. The main difference of course is that all property declarations end up in the final ruleset. But property accessors essentially "select" the property value using the same rules as variables, with the last one in the ruleset winning. |
More on properties: Originally, values assigned to variables were parsed into their individual nodes, whereas property values were essentially anonymous node strings, I think if they didn't contain any variables or functions. There was a (rightful) concern that adding property accessors would inflate the time / size of node parsing, since all nodes would be granularly parsed. What I did in 3.0 was add "late parsing" to the "late evaluation" model of Less. So, now, in the eval stage, nodes can run strings back through the parser if some operation needs to be performed. So that allowed properties to act like variables without instantly making the parse tree more complex with more nodes. |
So... Since |
Wait what?? |
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Layout_using_Named_Grid_Lines 🤔 Hmm...... @seven-phases-max as far as I can tell, there isn't any conflict here. The Less parser would require a preceding identifier (with no space) The CSS Grid syntax won't allow |
Added (failing) tests for namespacing. No work has been done / started on the parsing / evaluation side. less/less.js@3642ca9 |
Yes, there's no major conflict ... yet. Well, not counting CSS requires no space there, so if this goes beyond My main concern though is a very different semantics of similar looking values in the same context grid-template-rows: [main-start] .main[content] [content-start]; looks pretty exotic so far but who knows where they will end with these -- |
🤔 No, I see where you're coming from, just from the readability standpoint. The no spaces required is not as big a deal but..... I understand it's avoidable. Hmm......... Okay, well we've got some choices here....
Some possibilities:
grid-template-rows: [main-start] ${.main[content]} [content-start]; Also, Thoughts? |
Note, val: @{@dr var}; // which one does @ belong with? Instead of trying to do some kind of magical transposition of applying the leading val: ${@dr[@var]}; // It's clear the identifier is @var within @dr and `${}` is a referencing lookup And then you can grab multiple levels deep: val: ${@dr1[@dr2][@var]}; And then if we do the mixin aliasing feature here, we could end up with something like..... #usage {
@theme: ${.my-theme-ns(red)};
@theme.some-mixin();
border: 1px solid ${@theme[@secondary]};
}
// Unless only this would be required; we can work that out separately
//#usage {
// @theme: .my-theme-ns(red);
// @theme.some-mixin();
// border: 1px solid ${@theme[@secondary]}; -- ${} only needed for single values, not rules?
//} The last one is spitballing, I just wanted to make sure we weren't screwing up that thread. |
Honestly? I always hated this |
Regarding option 1 of I'd like to suggest a 5th option: The |
:) |
Haha fair enough, and I came back today to be like, "uuuuugh do we REALLY have to wrap all namespaced properties"? For the same reason, I never considered Usage: .test {
val: @dr<@var>;
val: @dr<@dr><@var>;
grid-template-rows: [main-start] .main<content> [content-start];
} vs. .test {
val: @dr|@var;
val: @dr|@dr|@var;
grid-template-rows: [main-start] .main|content [content-start];
} 🤔 The pipe 🤔 🤔 🤔 I dunno, the pipe is kinda fucking cool. Very clean result. vs..... .test {
val: @dr->@var;
val: @dr->@dr->@var;
grid-template-rows: [main-start] .main->content [content-start];
} HMMMMMMMM... the explicit nature of And So yeah, I would agree with all 3 of these options over |
Ha, I noticed after posting that Github did some good code coloring with |
The nice thing about .test {
val: #ns > .mixin -> @var;
} vs. .test {
val: #ns > .mixin <@var>;
} That is, when space-separated, the latter starts to look more strange and inconsistent. |
These
So please do not treat these as some kind of proposal (they were really just random inspirational goodies to stress individual habits. :)
That's another story. There're reasons (and this is one of them) why in either C-like language you (ideally) always write member-access and subscript operators w/o spaces. E.g. in JavaScript:
is valid (if I'm not mistaken) but we never write it like this :) (it's always |
[sigh] Okay. Well... suggestions? |
I still do like |
lol @seven-phases-max sometimes it's hard to glean what your actual position is. 😉 Okay..... if |
:) div {
color: some-ns(primary); // == .some-ns[@primary]
}
.some-ns {
@primary: red;
@secondary: blue;
// etc.
}
// map namespace to function using corresponding plugin:
.function-some-ns(@ident) {
.some-ns;
return: @@ident;
} which will be sad counting the efforts (to be) taken. |
Please check out my PR! less/less.js#3242 |
Question: could we not apply the concept of lookups to lists? That is, why not allow it as a shorthand for: @items: 1 2 3;
@value: extract(@items, 2); // old
@value: @items[2]; // new I mean, the evaluation is really not that hard. Most of the work has been done. So, like JavaScript, it could refer to index-based or key-based lookups. The only confusion might be that I think Less lists are 1-based, not 0-based? But I think I would still keep it consistent with whatever |
Saying I think I'd want to deprecate (¿or maybe just discourage?) Got my 👍, but I don't think it's super critical, unless it turns out to be low-hanging fruit. |
@calvinjuarez Well it parallelizes other languages like PHP and JavaScript which overlap arrays / maps / objects in the way they're accessed. So yeah, if we imagine every list as having a
They weren't related, until I started to use rulesets as maps for writing up 3.5 examples. Once you start to think of rulesets as just data, which may produce rules, but may be considered a map of values, then lists start to take on a semantically similar vibe. What I mean is, in Less, a "list" may just be a property value of a sequence of keywords, or it may be "used" as data for some Less functional purpose. In other words, Less already "re-purposes" CSS structure to produce "data". Trust me, start using 3.5 syntax and you'll see what I mean lol. |
Btw, I ran into this today related to this comment from @seven-phases-max. To summarize his comment: mixins technically merge their rules together, and that includes their variables. But the values of mixins get evaluated before merge. So even before 3.5, if you wrote: .mixin() {
@value-1: 1;
@value-2: @value-1;
}
.mixin() {
@value-1: 2;
}
.foo {
.mixin();
bar: @value-2;
}
// output
.foo {
bar: 1;
} This problem isn't resolved in 3.5, To be honest, I don't know that we want to change the behavior, especially with the goal of making vars private in the #16 discussion. But we may need to be careful to document that overriding a value for a variable will not change the computed value of other vars. Instead, users should be encouraged to evaluate the computed values at call time. I basically "re-discovered" this problem when tinkering with the Less library that I'm using to test out this feature. |
@matthew-dean No, no, to reiterate, I have no objections. In my comment I'm stating essentially "Yeah, this all makes a lot of sense". I was thinking inline, though, so it's a little meandering. Sorry about that. I'm on-board. As to the value evaluation, I think that's a quirk and feature of Less. I'm not sure I'd want it changed. .mixin() { // default
@var: foo;
}
.mixin() when (@i-want-var-to-be-bar) {
@var: bar;
}
//...
@i-want-var-to-be-bar: true;
.selector {
-less-show: .mixin()[@var];
} .selector {
-less-show: bar;
} Right? Update: So, then, .mixin() { // default
@var: foo;
-less-show: @var;
}
.mixin() when (@i-want-var-to-be-bar) {
@var: bar;
}
//...
@i-want-var-to-be-bar: true;
.selector {
.mixin();
} .selector {
-less-show: bar;
} They're essentially the same, right? |
The discussion around namespacing had some proposals on syntax, but I'm starting to think less/less.js#2767 makes the proposal problematic. less/less.js#1848
Yes, that was part of the namespacing discussion. However, after some of our discussion around unifying mixins and detached ruleset behavior, I'm not sure the syntax really makes sense.
Namespacing
For example, we were talking about:
...with var and prop a variable and property inside that namespace, and the
@
and$
applying to the last identifier. But if we make a detached ruleset a kind of (anonymous mixin assigned to a var), then this logic begins to quickly get confusing.We also talked about the short form
#ns@var
, which becomes something like@dr1@dr2@var
, which conflicts with other syntax.So, I'm thinking for 3.0, we re-think it to one of the earlier suggestions by @lukeapage which was close to the original Ruby syntax with
[]
for properties:That places the
@
symbol closer to the actual variable, and makes the identifier it belongs with less ambiguous.Interpolation
... Discussion moved to #13, as it's not necessary to do both at the same time, or at all.
The text was updated successfully, but these errors were encountered: