Skip to content
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

[Feature Request] Add 'rand' to Math Functions #2856

Closed
luxlogica opened this issue Mar 30, 2016 · 16 comments
Closed

[Feature Request] Add 'rand' to Math Functions #2856

luxlogica opened this issue Mar 30, 2016 · 16 comments

Comments

@luxlogica
Copy link

It would be incredibly useful to be able to generate a random number - specially during development and testing, such as when we want to:

  • automatically generate a random colour
@randColor: rgb(rand(255),rand(255),rand(255));

.panel {
    background-color: @randColor;
    color: contrast(@randColor);
}
  • pick randomly from a selection of pre-defined styles:
@randStyle: rand(2);

.my-element1 {
    .testStyle(@randStyle);
}

.testStyle(@which) when (@which = 0) {
   // style definitions #1
}
.testStyle(@which) when (@which = 1) {
    // style definitions #2
}
.testStyle(@which) when (@which = 2) {
    // style definitions #3
}
  • access online API urls - such as Unsplash.it - with random parameters
@randImg: rand(1050);

.hero-banner {
    background-image: url("http://unsplash.it/500/300?image=@{randImg}");
}

The Math Functions provided by LESS are already pretty extensive, and the addition of a 'random' function to the core would make it pretty close to complete!

@Synchro
Copy link
Member

Synchro commented Mar 30, 2016

This was already requested (with a PR) by me some years ago in #732, and it was requested and rejected since then in #2595 as well - in short, yes, but write it as a plugin.

@luxlogica
Copy link
Author

Sorry, but as a non-JS-programmer user who likes LESS for its simplicity, the suggestion I should go and write a plug-in is a bit unhelpful.

@Synchro, I took the trouble of reading the thread in your original PR, and it seems the whole discussion is a bit strange. You proposed a change, and it seemed to have been rejected on the basis that it 'compromised LESS' simplicity'. Comments afterwards suggested that people were getting in trouble with JS, trying to implemente the feature on their own. Since then, you yourself stated that others have made the suggestion, and have been shot down - it seems the user in issue #2595 did end up going and writing a plugin on their own.

And as if this was not sufficient evidence that a rand function is needed, we know that SASS added a random function a couple of years ago...

It seems that the decision of keeping a 'rand' function out of LESS is not being based on need, or evidence - specially when contributors like you had already done the work. For someone like me, it's very frustrating, and disappointing to see.

@seven-phases-max
Copy link
Member

the suggestion I should go and write a plug-in is a bit unhelpful.

It's not really limited to you to write a plugin. If you're not a familiar with JS, then in general there's no difference for you to wait for someone else to write this either as a plugin or in the core.

It seems that the decision of keeping a 'rand' function out of LESS is not being based on need, or evidence

Whatever not-so-for-every-day-use functions (see below) are usually decided to be supported via plugins simply because it's physically impossible and/or too tedious to maintain "every thing people of the third planet of our star system can imagine all-in-one monster". This is actually why the plugin subsystem was developed at all, i.e. instead of endless disputing if a particular function is brave enough to burden the core or not, the definitive answer now is "write a plugin and start to use what you need right now".

And SASS added a random function a couple of years ago...

This argument never works here (Less language development approach is almost opposite to Sass one).


Specifically for "not-so-for-every-day-use" case. random is indeed probably the most weird thing to put in the core because of how it will actually apply. It will be called once you compile your Less code to CSS (unless you're compiling client-side which is strongly not recommended by now), so that "random" styles and/or unsplash.it images become totally not-random for your pages anymore (unless you recompile your Less code again), so these use cases do not make too much sense from practical point of view (in fact selecting just "next" style from list of styles or 123 image from unsplash.it will give the same or better result).

And the random color example itself is quite arthificial use-case (e.g. if you don't care of .panel colors don't touch them at all, and if you do care (which is supposed to be since you are developing a style(sheet)) then yet again simply choose a "next" color from a predefined list of colors of whatever palette/theme you like).


Either way, if you was patient and read down to this paragraph, here's the plugin code:

functions.addMultiple({
    random: function() {
        return new tree.Dimension(Math.random());
    }
});

just save this code as (for example) my-funcs.js, put @plugin "my-funcs.js"; into your Less code, and voilà.
(See now what this "plugin thing" is about? it's less text and time than you spent above to argue ;).

@matthew-dean
Copy link
Member

@seven-phases-max You can actually write @plugin "my-funcs"; with .js as the default extension. 😉

@matthew-dean
Copy link
Member

To be fair to @luxlogica, plugins have been fairly opaque in Less to this point, although my hope is that will change significantly in the next 6 months.

@luxlogica
Copy link
Author

@seven-phases-max, thank you for taking the time to provide an explanation, and to provide the code for the plugin. As @matthew-dean stated above, if the intention of the developers is to have all edge-cases covered solely by plugins, then their use and implementation should be thoroughly documented.

Your arguments against putting a 'rand' function in the core seem to come to 2 points:

  1. 'rand' is an "edge-case" function that is of very limited usefulness for LESS users in their compile workflow

  2. it would be a burden - i.e., "impossible" and "tedious" - to maintain such a function in the core

IS 'RANDOM' AN EDGE-CASE?
As LESS' own issue tracker shows, over the years people have requested it. Other preprocessors have implemented it - and you can see that their users were calling for it, too. The need is obviously there, and is not as much an 'edge-case' as esoteric trigonometric functions - such as asin/acos/atan - which are already in the core.

Also, you seem to say that if a function would only be useful in the browser context, then there would be no use for it in the LESS core. This sounds odd to me, as it seems to go directly against what I had understood was the philosophy of LESS: that everything in it was designed to run just as well on the browser, as on the command-line. The LESS homepage states quite proudly that LESS runs on the browser. Indeed, I've seen CMSs where the admin panel uses in-browser LESS, so users can customise their templates and see the results straight away. Imagine how useful a 'rand' function would be in those contexts, too.

THE CODE WOULD BE A BURDEN TO MAINTAIN
Lastly, it seems you've proven just how easy it is to add such a random function, yourself. It seems to me you produced a 3-line piece of code, which must have taken you all of 5 minutes. It doesn't look like it's something that would require any maintenance at all, let alone be 'impossible' or 'tedious'. After posting my comment here, I actually looked through StackOverflow, and found more posts from other LESS users, who wanted a random function. These actually suggested a single-line solution, which involved calling javascript built-in 'Math.random()' to be parsed inline by LESS. I didn't even know this was possible, as javascript parsing doesn't seem to be included in the documentation. But again, this begs the question, that if this can be achieved with a single line of code, which is actually already part of the javascript core, then why hasn't this been simply added to LESS, while other esoteric functions have?

The decision seems arbitrary, and disappointing.

@matthew-dean
Copy link
Member

@luxlogica The decision isn't arbitrary. In general, the shift for Less has been to strengthen a plugin model. That means that features that are just functions that simply return a value, are not features that address any syntax improvements / changes are now encouraged as a plugin.

The documentation portion of that is an issue, as the volunteer support for documentation has been light. But it wouldn't be prudent to solve lack of documentation by abandoning an evolution strategy. And @seven-phases-max has been good enough to write exactly what that plugin would look like, which would address the issue immediately without a new build of Less.

"It's just one more feature" is not a compelling reason to add one. A library like Less.js is a collection of "it's just one more feature" and decisions about maintainability have to be about the list of features as a whole, as well as policy for adding more. The plugin direction is an example of that; that is, a strategy for improving long-term maintenance and reducing the amount of small-use-case requests and library changes. It's a good one; you're going to like it, and the more its supported & developed, the more that plugins like this will already be written / available. Deciding to make an exception based on function length would be arbitrary.

So, instead of thinking of it has your idea shot down, just compare the workflows from here. In the past, function requests went like this:

  1. User makes a request for function.
  2. Less community discusses, core team endorses.
  3. A community member makes a PR, code is released.
  4. Feature is advertised on the Less website.

Here is the plugin workflow:

  1. User makes a request for function.
  2. Less community discusses, core team endorses as plugin.
  3. A community member makes a PR, code is released.
  4. Plugin is advertised on the Less website.

The only thing that has really changed is:
a) The repository of the function.
b) Someone who does not need that particular function (or collection of functions) doesn't have to include/download it.

So, yes, absolutely, this code should be written and released, and when it is, it will show up on Less's list of plugins. In fact, it's possible as plugins are developed, Less may maintain a list of recommended plugins. But we want to keep feature growth to a minimum, to maintain the health of Less. Look at Sass as an example. It got so heavy and burdened with features, that the original compiler could no longer keep up, and it had to be re-written in a low-level programming language just to get the compiler to function adequately. Having to do that worked out well for Sass, but the library was basically forced into that position by adding too many features for a JIT language.

Anyway, that's some more explanation, and if you want to discuss more, there's a discussion forum on Gitter: https://gitter.im/less/less.js

Hope that helps.

@luxlogica
Copy link
Author

@matthew-dean thank you for your explanation - it's truly appreciated. I can understand the rationale for moving simple, value-returning functions that don't add features to the language out of the core, and into plugins. Unfortunately, you will have a problem when it comes to core Math functions like rand, as many have already been implemented in the core, while others haven't. So if you want to be consistent, you'll either should implement all core Match functions natively - which rand should possibly be a part of - or move them all out onto plugins (such as trig, and perhaps even the color-manipulation functions). Neither solution is ideal, really.

For the end-user, using a framework with lots of plugins is not necessarily easier, or better. There are users that like modular frameworks, and there are those that prefer monolithic ones, so I feel your pain: whatever the decision is that you make, you'll get flamed by the other team.

I love LESS for its semantic closeness to CSS - makes it very easy to introduce new frontend developers to CSS preprocessors. I'm sure that whatever solution you guys come up with, it'll maintain LESS' spirit of feature-completeness, and ease-of-use.

@matthew-dean
Copy link
Member

@luxlogica Are you talking about all the functions on the JavaScript Math object? We don't have all of the rest of them.

I took a look at the related Less library code. It turns out that there's just a whitelist of functions on the Math object. Check it out:

var mathFunctions = {
    // name,  unit
    ceil:  null,
    floor: null,
    sqrt:  null,
    abs:   null,
    tan:   "",
    sin:   "",
    cos:   "",
    atan:  "rad",
    asin:  "rad",
    acos:  "rad"
};

for (var f in mathFunctions) {
    if (mathFunctions.hasOwnProperty(f)) {
        mathFunctions[f] = mathHelper._math.bind(null, Math[f], mathFunctions[f]);
    }
}

So to add random would literally be the number of characters in the word "random", plus quotes. Sooo... Seeing that, I'm inclined to support it in core, because that's about as low a burden as you can get.

However, it would still need a related test added.

@seven-phases-max Are you okay with that? It's not a function that isn't already on the Math object.

@matthew-dean
Copy link
Member

One note about that. If we whitelisted "random", it wouldn't behave like your original example. It returns a number between 0 and 1, so you would still need to multiply it by another value, and probably round the result. If you want a rand() function to behave differently from Math.random() then we're probably in plugin territory again.

@seven-phases-max
Copy link
Member

@seven-phases-max Are you okay with that? It's not a function that isn't already on the Math object.

I'm OK if we then expose all of 44 properties/function of the JavaScript Math... I can't see what is so special about random.

@matthew-dean
Copy link
Member

... Even if they're not being asked for? o_O That seems like burning the house down to light a match. We don't even have the concept of math properties. Similarly, I'm not sure what's special about atan, but we expose it. I would wager a guess that themes use random selection of UI elements rather than calculate their atan values, whatever that would be used for.

But I won't push it, if there's not consensus from the core group. I just kinda wondered if we were being too principled when I saw that it was literally the difference of one keyword. Just a thought.

@luxlogica
Copy link
Author

random is extremely useful - if for nothing else, during testing of interfaces: being able to randomly load different colours, pictures and elements, and see how the interface reacts to these, is invaluable.

As mentioned before, there are also use-cases where LESS is being used in-browser, where random would be extremely useful. As @matthew-dean mentioned, certainly much more useful to the average developer than atan, which is already in core.

@seven-phases-max
Copy link
Member

random is extremely useful

And here again we came to #2618 (comment) (last paragraph).

certainly much more useful to the average developer than atan, which is already in core.

Something useless/broken already in the core should not be used as an excuse to add more garbage in. The new garbage then will be used as an excuse to add another garbage and so on and so on. Don't even start.


@matthew-dean After all, what exactly did change since #732 and #2595? The decision is made at those times, and I can't see anything new that can be used to re-evaluate it.

@luxlogica
Copy link
Author

@seven-phases-max So, regardless of the fact your own developer states just how easy it would be to add this, and how many reasons your users can give you to want it, and the fact that 3 separate issues have been raised so far asking for it, you're still just going to say 'no', because "we already said no before"?

I think @matthew-dean said it better:

(...) I just kinda wondered if we were being too principled when I saw that it was literally the difference of one keyword. Just a thought.

@matthew-dean
Copy link
Member

@seven-phases-max Nothing changed. On principle, you are correct, and I do agree with the principle. And also agree that probably this thread is too much energy to spend on what is an easy-to-implement plugin. Was just trying to do @luxlogica a solid, since Math functions are in, just whitelisted. But, on the other hand, you already did that by providing the exact code to include in the project. I can see both sides of it.

But as I said, if there's not consensus, it stays out. Which is also a good principle for avoiding bloat. Sorry @luxlogica. This issue will remain closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants