-
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
Mixin return values #538
Comments
Oh I see, no. But you can do.. .myselector () {
@return: 2px;
}
.class {
.myselector;
border: @return solid red;
} pretty ugly though. |
Ah, of course. It might be nice to have a returned value for custom color functions (and other custom value functions) when mixin guards are in place. @return could be a reserved variable for LESS. As you say, it's a bit ugly to just have a mixin do nothing but variable assignment when it's called. Would be nicer to see the first example I posted supported at some point. The rationale being that you have spin, lighten, darken, etc functioning as built-in mixins, so the logic for return values is already there in LESS, it would just be opening up the door for custom mixins. |
Not I think that programming said functionality is easy mind you, just that it would empower people to extend the logic a bit. |
yes, true, but I also like how it currently is, where the 'functions' are all javascript.. |
Couldn't variables accept arguments the same way mixins do? My usecase would be for instance:
It's also fairly analogous with JavaScript where functions live in regular variables. |
@cloudhead: If I spent some time implementing that feature (I haven't looked at the Less source yet, so I can't promise anything), would you be willing to add it if the code is good enough? |
Adding a |
duplicate of #538 |
woops, this is #538 |
This would be a very, very useful solution. I'm trying to implement dynamically resizing images (like Curently I'm using @cloudhead's solution. |
Just to tie this in a nice bow, this should be included when the options.json support is added. Issue: #850 |
Mixins are mixins, we must not conflate their usage with functions. If having the ability to define custom functions in the style sheet is something we want though, that can be a separate discussion. |
The problem is that there's no way to forbid using mixins as functions. It is possible to use one as the other and they (and me :) get to use that ability. So de facto it's only a matter of syntactic sugar and verbosity:
Using same "function" twice in the "same scope":
Is there a reason to invent some new "Less function declaration/definition" syntax (different from a mixin definition syntax) only to not allow to use |
What the what did you just do there. That's some next-level Less-hackery going on there, lol. |
@lukeapage I am sorry I can't understand the state of this issue. You've put a reference to this issue on several others but this is set as closed. Is there a plan to allow to use mixins as lambdas or are you suggesting us to take advantage of the global variable setting workaround? In other issue I've understand you were planning to remove that on some future. |
@seven-phases-max your approach is awesome but there is no way to pass to the sum mixin the result of calling sum two times, am I wrong? |
@axelcostaspena Well, everything is possible using mixins, it's just a matter of verbosity (obviously 5 or 6 lines of code do not look like a practical replacement for a oneliner). |
@seven-phases-max I'm still skeptical, and I feel like the burden of proof is on the multi-line examples. That said, But a return statement feels less and less like Less every passing moment. As to this:
The question is better stated: what is the compelling reason to add more syntax to the language just to split that expression onto two lines? The burden should be to prove a more verbose syntax with extra keywords is necessary, not the other way around. There's nothing "normal" about it, as it doesn't exist yet. And it's still not close to a real-world example. @battlesnake's example was still only a single-line expression.
I like the idea of functions, but it would be disappointing if we added something heavier to the language than was absolutely needed / useful. We already have 3 types of blocks that "do things": mixins, classes/ids than can be "evaluated" and imported into scope (which makes them sort of non-parameter mixins, but they are somewhat different), and detached rulesets. If you add a functions block, all of these definitions have inconsistent syntax from each other. A mixin isn't defined with an I think this should take a step back and look at the syntax as a whole. A |
That is, just consider how this looks for a moment:
I know that a function must be specially defined to distinguish between other blocks, but maybe that's the problem. If function says |
@matthew-dean I'm a bit lost then. So how exactly @function web2-gradient(@direction, @start, @end, @split: 50%, @spread: 5%)
: linear-gradient(@direction, @start 0%, @start ((@split-@spread)), @end ((@split+@spread)), @end 100%) instead of @function web2-gradient(@direction, @start, @end, @split: 50%, @spread: 5%) {
return: linear-gradient(@direction, @start 0%, @start ((@split-@spread)), @end ((@split+@spread)), @end 100%);
} solves all that problems you mentioned? Maybe then it would make sense to start with questioning the I did not comment To be honest personally I'd be more happy with either
? So you don't really see any similarity between Less mixins and (whatever syntax) functions? I can guess many users do not understand parametric mixins at all thinking of them as some kind of a primitive "CSS-properties-batch" only, but in this particular function/mixin context, hmm... Either way:
can be potentially better to justify most likely more bloating implementation (+ new parsing code) to provide more limited functionality? And finally, to back up "feature/syntax consistency" I guess that a reformatted table of existing Less feaures the way I see them would be interesting (just in case): name {prop: val} // a ruleset having some standard CSS selector as a name
@name: value; // variable
@name: {prop: val}; // *unnamed* ruleset assigned to a variable as its value
// mixin
.name(@x, @y) {
prop: @x * @y;
}
// function (whatever `func` directive)
<func> name(@x, @y) {
return: @x * @y; // return property of the function
} |
Having no prefix for function names would be consistent with the current state of LESS, i.e.
Having no prefix for JS functions, while requiring a prefix for functions supplied in the actual LESS code seems somewhat backwards. I personally don't see any use for multi-line functions, but I may be having a "who needs >640k of RAM" moment here. If there was a demonstrable need for multi-line functions in future, after we had adopted the
That syntax would still be Variables in such blocks are scoped to the expression following the block only, they don't leak out to parent scope. This way we could add single-expression functions now, but we still have the option to extend them to multi-line in future (if a need was demonstrated), by either using "recipe" block-style functions (with If block-style was to be used, I like the |
Everything is ruleset-like, including functions, which just look exactly the same as mixins. Really, when boiled down, the sole discriminators are that mixins can set CSS properties and that functions have to have a return statement. (You cannot and should not do both.) So why not just rely on that behavior directly? E.g. .mixin(@a, @b) {
value : @a @b;
}
.func(@a, @b) {
return @a @b;
} Then let the compiler figure out at the call site whether it is calling a mixin or a function and raising an exception if one or the other is used in an inappropriate context. Alternatively, if exceptions aren't your thing:
|
Hmm polymorphism... it makes sense I guess, mixins can't appear on the right-hand side of a colon and functions can only appear there. As long as we ensure that errors are raised if one is used in the wrong place, using mixin syntax for functions seems ok. |
This is what I was getting at. Should we create a new ruleset type that has a different method of "define"? But... I also agree with this:
I've always felt that the requirement that a mixin be prefixed with However, there is some reasoning to have a separate type, since function vars should not be imported into the caller scope, and functions should not have guards, as it's hard to intuit what that would even mean. So there are other differences other than being able to return values. I'm not necessarily against Even CSS breaks it's own rules sometimes, and contains things like having the I'm a fan of limiting to single-line expressions, but if it seems too arbitrary and restrictive to others, then the multi-line form that seems most clear probably is indeed:
Maybe the best argument for a dedicated "define" is that, different from mixins, a function definition can't or shouldn't appear multiple times (in the same scope). So they're more like |
Why not allow literal javascript inside less? Basically, it'll allow you use all javascript features inside CSS to create functions. Example:
Note two things:
|
@rentalhost
Secondary, Less is designed as a CSS superset, and CSS has very few to do with C-like languages and JavaScript in particular. Thus direct JavaScript statements like So two basic reasons, why a code like above should never be recommended to use and a JS-like syntax should never be considered when designing Less syntax, are obvious:
In other words, all our discussions above are not a result of so called "not invented here" syndrome, but just an attempt to fit possible function definition syntax to already existing Less syntax-base as smoothly as possible. |
Please please please: .halfway-between(@large, @small) {
max(@large, @small) - (abs(@large - @small) / 2);
} 😃 |
@andrewhosgood What happens here? .halfway-between(@large, @small) {
max(@large, @small) - (abs(@large - @small) / 2);
}
.halfway-between(@large, @small) {
property: value;
foo: bar;
}
.rule {
width: halfway-between(3, 2);
} Ultimately, a mixin is a parameterized ruleset that can have multiple matches, and returns multiple values, and a function name can only have one match (per scope) and can only return one value. I think @battlesnake had the simplest and most straightforward syntax proposal: @function square(@x): @x * @x; A slight adjustment so that the scope of @function square(@x) { @x * @x; } Or (possibly) so that we don't confuse with JS-based functions: @expression square(@x) { @x * @x; } I think single-use expressions would be simplest to grok, and wouldn't pollute understanding of mixins. And, would also serve most of the examples given. Basically, you're just short-handing (aliasing) expressions for quick re-use. Thoughts? I do wonder if there's a way to "define" the expression name without an at-rule, though. Hmm. |
@matthew-dean Point taken. Just trying to use existing patterns. Amending my request then, would it be: @function halfway-between(@large, @small) {
max(@large, @small) - (abs(@large - @small) / 2);
} Not worried about syntax, but this would then allow me to do the thing I can't do currently. |
Syntax is the hardest part to get right. And you want to get it right because it's a new language feature. I'm a little wary of
Thinking over it more, I think the colon I like this simpler concept of re-usable expressions rather than "functions" (which already means "defined by the parsing language" i.e. JavaScript). So |
Basically; they're parametrized pre-processor macro expansions like in plain old C, right? But hopefully without the crazy abuse that came with those, since Less will have |
#538 (comment). Though never mind. And I still object the inventation of the special syntax hardcoded to specific "one liner" examples. Still smells like "I love to invent new cool syntaxes" syndrome. {
foo();
foo() - bar();
bar();
} becomes actually. There you'll have to introduce a lot of new parsing rules to detect which statement is actually the function returning value and which are just root function calls (not even counting a friendly error messages if something goes wrong). And yes, stressing the Sassish |
{
foo();
foo() - bar();
bar();
} I was implying that multi-statements would be invalid here (would evaluate to a single expression), so that wouldn't apply, BUT....
I haven't been using PostCSS, but yes, writing both .function-sqralpha(@c) {
@a: alpha(@c);
return @a * @a;
} .function-sqralpha(@c) {
return @a * @a;
@a: alpha(@c);
} So, either way, with what's discussed so far, it seems somewhat square peg / round hole. I don't think mixins can reasonably pretend to be functions (without some compromises and unexpected behavior), and defining functions (or expressions or macro expansions) via Less (that is, in your stylesheet) has proven to also be problematic. On a separate note- One thing that might lean us even a bit more to closing this thread: when I was looking back through comments, including the one you linked to, you had this as a not an ideal way to solve the above problem. functions.addMultiple({
sqralpha : function(c) {
var a = c.alpha; // no docs and no guaranty this internal stays the same, but there's no easy way to reuse built-in Less `alpha()` here
return new tree.Dimension(a * a); // Dimension? some kind of quantum mechanics?
}
}); With Less 3.0, I've simplified returns so that any non-Node (other than falsey values) returned by a function is cast to an Anonymous Node, such that it's easily output into your final CSS. Obviously, if you want to include that result in another expression, you need to be explicit in the Node type. Buuuuut.... 3.0 also includes a "late parsing" feature (all values are Anonymous until referenced - this allows property referencing without creating extra parsing work or extra nodes), so that could be repurposed in order to just be like: functions.addMultiple({
sqralpha : function(c) {
var a = c.alpha; // docs have started! doing my best!
return a * a; // Cast to Dimension because Less is cool... maybe?
}
}); And considering 3.0 should be all about functions and |
No no; you raise a good point. There's substantial overhead for the middle-ground, where you have more than a simple one-liner expression to do, but not enough to warrant firing up a JS plugin and writing code that handles all the node-type mappings. While @matthew-dean raises a good point that node return types could be inferred, that still feels like a lot of overhead for some simple math or string interpolation. (In particular the latter; string interpolation requires a temporary variable, so you can never write a one-line expression for it.) If working a usable return into the syntax is the problem, then why not skip the explicit return statement altogether by specifying what name the out-variable will be bound to: .answer(@a, @b):@c {
@c : ~"The answer is @{sum}";
@sum : @a + @b;
} This feels natural with the order independence and declarative nature of the Less language. It also directly extends the signature of mixin declarations in a non-ambiguous manner, which the parser can look for and which avoids the need for an This also means it can work together with mixin overloading and multicast calls. If a mixin is called like a function, it requires that a declaration exists which has both a matching parameter signature and a colon-separated out parameter specified. And if multiple matching overloads exist, the return value becomes a list of the relevant calls in mixin declaration order. (Just as how multiple matching overloads currently output rules in declaration order.) |
Huh. That's actually not so bad. Question: what happens then with properties, values, and other variables declared in the mixin (if called as a function). Safe to presume they're isolated from the caller scope (since properties couldn't be omitted in the location of the call, if the call is within a value expression)? That's not bad. We expanded functions to be called everywhere. So it could be that mixins could be called everywhere with the same checks to see if the resulting output node is legal in that location. So then functions = JS and mixins = LESS. I also liked how you addressed and resolved multiple matching mixins (a list as the result). Clever. And declarative. I was ready to give up on this idea but this is really not bad. |
We could also update the documentation that mixins, by default, "outputs" a ruleset, unless an output var (or vars, or $properties??) are specified. |
Has there been an update for this? |
@distransient There has been more in-depth discussion around designing solutions for this in: less/less-meta#16. (Less-meta is a repo for higher-level goal planning for Less.js, rather than individual issues or bug reports.) |
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. |
Can I set a return value as a color? As in:
Just wondering in response to another user's issue regarding color functions: #488
The text was updated successfully, but these errors were encountered: