-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Allow variable redefinition inside guarded self running blocks #2072
Comments
That's a difficult one to decide. Personally I get accustomed to think of
I can see the benefits of turning the [1].
(typical use-case) [2].
W/o above features the |
So would |
i think (vote) that The result of In the current situation:
compiles into:
and:
into:
I think / expect both situation should have the same result:
Also see: http://stackoverflow.com/questions/29496933/less-conditional-variable-change-inside-mixin/29497626 ** some update **
But still expected |
Very into |
I prefer @seven-phases-max version, where Plus, I like that |
Btw. my opinion about this feature shifted from somewhat neutral (as I expressed above) to "strictly no". This happened as I stepped into a few related Qs here and there at the SO and one issue ticket at the tracker. Now when I better see why exactly people tend to use it this way and how exactly they are going to use it... I can find more concerning pitfalls it will bring in (+ a few problems I missed earlier). So in summary:
---In details. [1] Let's start with the following example: .a {
result: @a;
@a: 2px;
& when (true) {
@a: 10px;
}
} As you can see in above comments and in the linked SO question (actually there're a lot of exactly the same snippets at the SO) they clearly expect this to result in [2] Evading current scoping rules isn't a problem itself, but for sure it becomes a nice example of those "nonsensical and non-orthogonal/non-uniform scoping rules" they're talking about (falsely by now, but much harder to argue after this change). So when it will come to explaining/documenting this stuff it will be really hard to find any rationale for "why [3] (Probably the most important, strangely I missed this earlier) As noticed by @SomMeri above (and earlier in #1877) this also will break the pattern of using [4] Blurring [5] We all know that the lazy-loading stuff (and the "declarativeness" of Less in general) is probably the last thing most new users (at least those with PHP/JavaScript/"name-your-scripting-language" background) become aware of. So we'll have to be prepared for all sorts of weird code like: div {
@a: 5;
a {
& when (@a > 5) {
@a: 2;
}
& when (@a < 3) {
@a: 10;
}
& when (@a > 6) and not(something-else) {
@a: boo!;
}
result: @a;
}
} to jump in, all marked as Less bugs. "WTF is this lazy-loading ur talking about? I have exactly the same code working perfect in PHP!" :). Really, just take any arbitrary ---So for the ending summary: While I still sympathize this (quite big new as it turns out to be actually) feature in general, I believe it should be Speaking of
|
@seven-phases-max thanks for your extended answer in the first place. I do really appreciate that. I will have to think about all this. For now it seems you says everything about a & should act the same, and i possible argue that A new ... for update |
In general I would be favorable to syntactic sugar around often used cases that are currently either long or hard to read. Things that are just syntactic sugar tend to be easy to implement if the syntax is designed right, so why not if they add real value to the user. The rules less respects so far and we might not want to break without good reason:
When user need to declare single variable, a simple //ternary operator is standard
@variable: when(condition, ifTrue, ifFalse);
@variable: if(condition, ifTrue, ifFalse);
@variable: ?(condition, ifTrue, ifFalse); When multiple variables depend on the same condition, we could use new symbol for non-scope creating blocks. For example if (condition) [
// variables defined here override local scope
] elif (condition) [
] else [
] It could be just a syntactic sugar for conditional mixins + slight different handling of return values. |
XSLT is a good reference for a declarative language that is still very functionally expressive. I'm not a fan of introducing big logic blocks. BUUUT single line evaluations would seem to fit, because we already use them in part for things like the contrast function:
This is basically the same as this pseudocode:
They both evaluate against a value, and return a result depending on the test. And, in truth, the second is probably easier to logically follow than the contrast function itself (it's self-documenting), and allows more expressiveness. So, to go back to @lukeapage's example:
This becomes:
It's a hell of a lot easier to read, and we don't have to dick around with |
Side note: spreadsheet functions are also a good example for declarative programming functions. |
Oh, and second side note: I just realized in the contrast example that I'm not sure if it's less than 30% or greater than 30% that results in the first value being returned, which emphasizes my point. I would use an |
Which is basically #1894. Btw., XSLT has P.S. Curously |
@seven-phases-max What would |
It can't because this would break
In other words it's still Speaking of the function name, I guess it does not really that important but certainly not |
Reading that. I don't agree with the conclusions that were made from the example. A simple variable conditional assignment is less verbose than a guarded mixin to simply assign a variable. But yes, the description of the need is the same, as is the rationale.
That's silly. There's no reason it would have to break alpha. They're not even the same keywords.
It isn't, actually?
Or... certainly if? Since that's an understood keyword for new users, as is the fact that the second argument is for true and the third is for else. That's pretty consistent in multiple environments.
This would be my "certainly not". This breaks CSS and LESS conventions of functions being defined by actual names, not symbols. This isn't an operation. Is that how you're reading this? This is a LESS function with three arguments. It's better compared to
Exactly. The metaphor to XSLT doesn't even make sense, since XSLT's choose / when would allow multiple whens, so that actually operates like mixin guards do now. LESS's mixin guard / default operator are almost an exact parallel to choose / when. But, let's not dive into how like / unlike it is to XSLT. That's not really relevant. This is just an if / then / else function. As @SomMeri says, extra keywords are not needed, if they don't distinguish any behavior from omitting them, nor do they clarify the function at all. And to use
Now you're just being ridiculous. |
It's not the What I'm trying to suggest is some practical solution which is less or more possible to build into existing compiler. This no way means that others can't keep polishing a magical syntax to appear in Less 5 ;)
Oh, common... P.S. to clarify [1]: more strictly speaking the parser will have to build a dedicated tree value of |
I don't understand. If the parser needs a special pass for functions, how is this a different case? How do plugins work if you can't send any value to a plugin, or define any function name in a plugin? Why would defining a new function affect current parsing rules (and if so, is that a known limitation of plugin custom functions)?
Wut? Are you reading it that way or suggesting someone would read it that way? Because... A 3-argument (or optional 2-argument) It's fine to say that it's difficult to implement with the current parser. That makes sense. But I think to say the naming of the function or its behavior is inherently confusing to new users vs. any other alternative is disingenuous. A single statement IF / THEN / ELSE evaluation should not be foreign to anyone. |
All right. Excel, filemaker, maridb, wolfram - I'll buy it, won't argue anymore... |
1.) If we add conditional assignent, we need also to add a way how to assign boolean value to variable. This is preferable:
over repeating the condition here:
The point being we need both one parameter version and three parameters version. Another consequence is that variables need to be able to hold also boolean values. 2.) Less uses comma
Following statement:
This could be read in three ways:
The consequence is that we need to use either |
@matthew-dean One difference between languages you linked and less is that those languages have overall more consistent syntax - mostly because they are not supposed to be addition over css.
Optimally, well designed grammar would not require parser to know what kind of function it is parsing, the abstract syntax tree generated from
Wolfram but also others on that list are mostly well designed grammars that follow most known best practices, do not have ambiguities etc. The Basically, less have all kind of heritage that cause similar construct to be more problematic. |
Yes, in my variant above it was considered to be like:
|
Btw., https://www.gnu.org/software/make/manual/html_node/Conditional-Functions.html (yes it's actually the two function variant similar to what I proposed not a three-args-if ;) gives an interesting idea of omitting parens for the |
@seven-phases-max The space is list separator too, some constructs may and up ambiguous |
@SomMeri - Your arguments make sense. The ambiguity problem isn't so much in terms of similarity to functions but consistency with other conditionals. Thanks for clarifying. And that helps me get what you were saying, @seven-phases-max So in other words, we already have this kind of conditional syntax that we'd want to support:
So, now I'm seeing the cope of the problem. Since a "when" guard or Less conditional, however you want to phrase it, can be verbose, it can be a bit of an issue. What if we flip this problem around, and go back to original source. What if we just make "guard blocks" that would, by definition, exist in the parent scope. Trying to stuff this into function syntax is awkward, as you noted with the use of commas. Therefore:
and for multiple whens:
It doesn't have the benefit of being as slim of a declaration of a single function statement, but since that clearly won't work anyway, this would be consistent with current That's kind of back-peddling on my part, but I think you've successfully demonstrated that a condensed syntax is flawed. |
Side note: I'm glad you guys are involved with Less. You've got good brains. |
1.) I meant that it is not possible to create a variable that would hold guard value, e.g. something like this:
But since you can put multiple things into that block, it probably does not matter much. 2.) You are right. Maybe it could be just syntactic sugar for mixin call? |
@SomMeri - Ah. Well, as I said, based on your examples and noting the use of a comma, I don't think the "function" form will work, so as a result, there's no need to put a guard value in a variable, which is an awkward idea anyway.
I wasn't suggesting literally a mixin call. I think there's value in a dedicated |
I actually liked the "function thing" because it does not suffer from [5]... Btw., I guess it's also would be important to remind that mixin-based stuff works more like
how |
On @when (@b = a) {
@a: 10px;
} @elwhen (@b = c) {
@a: 20px;
} @elwhen (@b = d) {
@a: 30px;
} @else {
@a: 40px;
} The advantage is that user will have less need to use the some condition in multiple conditional blocks - less repetition is always good. On @seven-phases-max example: I do not think This is how I would rewrite it: .something(@a, @b) {
//mixin 1
@x: @a;
@when (@a=2) {
@x: (@a - @b) / 2;
}
//mixin 2
@when (@x < 0) {
@y: @b + @a;
} @else { // here would be condition repetition if when does not have @else
@y: @b;
}
}
div {
.something(2, 3);
} without else (but I prefer with else): .something(@a, @b) {
//mixin 1
@x: @a;
@when (@a=2) {
@x: (@a - @b) / 2;
}
//mixin 2
@when (@x < 0) {
@y: @b + @a;
}
@when not (@x < 0) {
@y: @b;
}
}
div {
.something(2, 3);
} Edit: edited typo in last code. |
Do you mean Pattern-matching? If, yes, then no, they are not (that would be 😱). |
Sooort of, but not really. A switch / case has single values, but I know what you mean. An if / then / else has a binary outcome. So yes, I was suggesting that, instead of using if / then / else, using the when pattern that mixins use. Meaning: two different
If we did that, we would have to change the way we write Less conditionals, not just add a method that evaluates those conditionals. @SomMeri An issue with your when / else is that your else is an independent statement. There is nothing that declaratively unites those two blocks. Even though you placed @else after the closing Therefore: I think that's when you would need a select or choose, and then you could use a consistent default (consistent with mixins).
Another way to illustrate that the "default" statement is independent is that you should be able to place the default statement like this (just as you can with mixins):
Each |
1.)
I am not sure what you mean by that.
If we define the statement as a statement with multiple blocks tied through keywords, then they will not be independent. if expression1:
statement(s)
elif expression2:
statement(s)
elif expression3:
statement(s)
else:
statement(s) Java supports only two blocks if (x==5) {
} else if (x==4) {
} else {
} 2.) If we do not give them @when (@x<10) {
@variable: 10px;
property-1: value-1;
}
@when (default()) {
@when (@x<20) {
@variable: 5px;
property-2: value-2;
}
@when (default()) {
@variable: 55px;
property-3: value-3;
@when (@x<40) {
@variable: auto;
property-4: value-4;
}
@when (default()) {
@variable: hi;
property-5: value-5;
//could nest further
}
}
} 3.) If we claim that Default being used before condition, because they are not tied in syntax - so I can: // sort of else
@when (default()) {
}
//things in between
property: value;
.mixin();
//condition
@when (condition) {
} Default being between two conditions, what now?: //first condition
@when (condition1) {
}
//things in between
property: value;
.mixin();
//else could belong to upper condition or to lower condition or be valid only if both are false
@when (default()) {
}
//things in between
another-property: another-value;
.mixin();
//second condition
@when (condition2) {
} If we say that Alternatively, we could put some code into compiler to make it clear which |
The examples you give are imperative patterns, which tell the compiler a precise control flow. If we are extending when from mixin syntax, it doesn't make sense to have a different control flow than used with mixins, or to suddenly include an imperative flow in a declarative language. To follow Less's model, you'd want to think of Similarly, all the examples of possible failures would apply to anywhere else where we currently use when guards, which no one has really complained about. In other words, I think you're describing a complicated issue which could exist, but there's no reason to think it does it already. And if in an edge case, someone wants to nest when statements, there's nothing more complicated to that than having a mixin call inside another mixin call, where both have guards. So, if these problematic patterns were to exist, they already exist, and this syntax wouldn't be introducing those problems. I don't think it's worth changing the nature of the Less language and declarative blocks in order to solve patterns people haven't asked about yet. In essence, what I'm describing is something like "anonymous guards". They're not bound to any selector block or mixin definition. They evaluate and return to their current scoped block. This would be a familiar addition to Less authors that are already using guards on mixins, which also has an advantage over the "functional" syntax discussed earlier. The pattern is already used extensively in Less. (However, it's possible that we could still also introduce a simple binary if/then/else function for simpler conditions and use cases. The problem would be how to write those conditions.) |
Also, another consideration could be a guard condition on the select statement.
|
@matthew-dean Mixins are named, so you can have one The examples are cases where you might want multiple different I do see how it changes the nature of less language itself. It just changes how the feature is implemented. We can not straightforwardly convert all |
A bit of offtopic, but somewhat related. I've just occasionally remembered an old trick that I think would be fun to share here (from "Branch-free based programming in Less", Chapter 6: "Table-Lookups"). |
Btw. (inspired by recent topics referenced above), since I'm not a great fan of the idea of bringing in :when (...) (with or without optional |
The only thing to remember is that That said, even having proposed So, ':when' is an interesting suggestion. I would suggest that as to this:
If we did that, then the same optionality should exist for |
Some other possibilities: 1.
|
I'd love to be able to do something like this: (when (@a = 1): { @x: 2; })();
// OR
@(when (@a = 1): { @x: 2; })();
// optionally without the `:`s I'm borrowing the JS anon self-calling function syntax, which seems most familiar and explicit to me. ALSO, Do the |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
In this case you get a syntax error from less. I propose it should work.
this case I am not sure about.
this should stay as an error.
@seven-phases-max and @SomMeri what do you think?
I was trying to make code that redefined a couple of variables based on an option.
I guess the other way of doing it would be this
and maybe thats just clearer?
The text was updated successfully, but these errors were encountered: