-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Add new mathematical operators #2887
Add new mathematical operators #2887
Conversation
…script into more-math-operators Conflicts: src/nodes.coffee test/operators.coffee
… of the new operators Also kill the empty regex :(
Nice work, @epidemian! |
This case could be fixed by requiring it to be either unspaced or dually-spaced, couldn't it ? |
Hmm... yes, but not without other nuances i think. When other operators appear at the end of a line they imply a line continuation: # Parsed as "foo(bar + baz);"
foo bar +
baz But with the # Parsed as "foo(bar(/(?:)/)); baz;"
foo bar //
baz (it seems the We could also opt for another symbol for floor division; though i would prefer it to be the same symbol as in Python. |
…on if they appear at the end of a line
I think the "easy" way to solve this is just use a different operator. Yes, "//" is the most make-sensical, and it's already well-known from python, but it saves a lot of effort to just use something different. I'll throw "/_" ("div-floor") out there, although it's neither better nor worse than most other alternatives. |
@bjmiller since |
@shesek and |
Sooo... any other thoughts on this PR? Any other operator that would be useful? What should we do with the
Then, after that release, we could finally add the new shiny Also, about the documentation, @jashkenas is good with the words, but maybe we can help providing some sample snippets for the new operators (bonus point for a coherent example that showcases all three of them 😸). I may fiddle with this later if i feel inspired =P |
Just double-checking: Is there a real use case for an empty regex? Like, why would anyone intentionally create one? Anyway, it's still my preference to come up with an operator that requires less work to implement. The problem is, I don't really see any other combination of symbols on the keyboard that make any kind of mnemonic sense to me. |
I have no idea. The
Implementing About the documentation for the new operators, maybe instead of examples we can have a simple table showing the JS equivalents:
Though, to be in tone with the rest of the documentation maybe some runnable examples would be better, i don't know. Some ideas: # For the modulo operator, maybe a circular array index.
arrAt = (arr, i) -> arr[i %% arr.length]
notes = ['do', 're', 'mi', 'fa', 'sol', 'la', 'si']
alert "The note before do is #{arrAt notes, -1}"
alert "And 7 notes after do is #{arrAt notes, 7} again!"
# For the floor division, any division that should be rounded would be good IMO.
seconds = (new Date - start) // 1000
alert "You have been reading the docs for #{seconds} seconds."
# (`start` could be taken when the page is loaded)
# For the power operator, maybe a statistical formula like exponential growth.
population = 7000
rate = 0.05
time = 10
finalPopulation = population * (1 + rate) ** time
alert "Population after 10 years: #{Math.floor endPopulation}"
# Not sure if a good example for the power operator, but i like it =P
φ = 1.618033988749895
ψ = -0.6180339887498949
# A fast fiboncci number formula.
fastFib = (n) -> (φ ** n - ψ ** n) / (φ - ψ)
alert "fib(8) = #{fastFib 8}" I think i would prefer the table for succinctness' sake though. (totally off-topic: why does the GH highlighter discriminate the awesome φ and ψ symbols with that ugly red background!?) |
Because pygments sucks. It has one job, and it can never get it right. At least it no longer thinks herecomments never end. |
@epidemian pygments believes non-ASCII characters are not valid in CoffeeScript (and presumably many other languages) source. a unicode travesty. |
So long you don't turn Coffee into APL :p. |
Perhaps not so related to this PR, but when I think about maths and CoffeeScript, this is what comes up in my mind: Maths and CoffeeScript teacher: “What is the result of |
@lydell: Agreed, the juxtaposition operator (implicit function application) should have higher precedence than all other infix operators. But that's a discussion for a different issue. edit: Also, you can do renaming in destructuring assignment: |
Thanks for clarifying, @michaelficarra, i wasn't sure what @lydell was pointing out. I'd appreciate if we could have some closure for this issue. Should i invoke @jashkenas? :) Jeremy: do you think this PR is acceptable? What do you think we should do about the clash between the floor operator and the empty regex ( |
@epidemian: I believe we were all happy about the power operator and floored division, but the max/min (which I'm strongly for) and fixed modulo operators were still undecided. |
@michaelficarra, thanks for the feedback. For the min/max operators, i guess you're referring to the LiveScript's cute For those who do not know, they are basically the operator form of I personally find these two operators to be quite intuitive and consistent with current binary That being said, the other operators included in this PR were chosen mostly for their "familiarity" in other languages as well as their convenience. In the case of In the case of All in all, my vote for the inclusion of these two operators is neutral. The popularity of the operators in other languages doesn't really bother me that much (chained comparisons are not that popular outside Coffee either, Python being the only exception that comes to mind, and they're damn useful for example). I'll not be sad if they are rejected, but i will happily implement them if they are accepted 😺 I'm interested in what other people think. |
I don't think I've used coco's More than 4 times in a year, so I don't dind them that attractive |
They're mostly for strings to begin with, as you should be using |
@xixixao I think probably go for it. My two questions are: Does the modulo operator really need a helper function, instead of being compiled inline? What are some good real-world use cases for the modulo operator, where the existing remainder operator |
Many cases, I have written the following pattern way too many times in my life: i = something
array[(i + array.length) % array.length] (notice that's not even bulletproof). +1 for inline compilation, @epidemian ? |
That's exactly what I mean — you're just using the remainder operator there, not @epidemian's proposed "correct" modulo operator. Where does the correct one work that the remainder won't help you? |
@jashkenas My code is error prone and longer than i = something
array[i %% array.length] Now the correct modulo not only fixes the case of negative left operand but also negative right operand. |
Why would you have a negative RHS? |
Why not? I mean that is not applicable to my example, but it is likely that some algorithm might depend on modulo with negative divisor. If not, we can compile to the more relaxed version. |
Original discussion on modulo starts with your comment here: #1971 (comment) |
That's exactly what I'm asking. Have any of you ever needed "true" modulo for real-world programming before? If so, what for? If not — we can just stick with |
Nope, it's just for convenience/laziness: a separate function makes it very easy to guarantee that each operator is evaluated only once. If we were to compile this in-line, we should assign the value the second operand (if it isn't a simple expressions) to an intermediate variable, because it's used more than once in the operation. A bit more complex, but yes, there should be no problem in doing so. I personally prefer having a separate helper function, as i think
Anything that "wraps around" and may be negative. For example, a "neighbours" function in a Game of Life (stolen from the first article i found...): neighbours = ([x, y]) ->
x0 = (x - 1) %% N
x1 = x
x2 = (x + 1) %% N
y0 = (y - 1) %% N
y1 = y
y2 = (y + 1) %% N
[[x1, y0], [x2, y0], [x2, y1], [x2, y2], [x1, y2], [x0, y2], [x0, y1], [x0, y0]] These kind of operations are pretty common in games :) If you would prefer to keep this operator out, i'd totally understand. I'm not sure either if having a correct modulo operation is worth the complexity of having a new operator. |
@epidemian Great example, let's leave it in then. And your argument about having it as a helper is persuasive as well. Sounds just fine. Feel free to fix up this PR so that it applies cleanly, and then merge away. |
Make sure to force numbers in the modulo helper. You'd get nonsenses like:
|
@satyr There is no way to effectively type check what a variable would be other than in their literal form. If you want to prevent them from being used in literal form, I really don't think there will ever be a case where someone would need/want that. |
@satyr |
I think it’s worth fixing @satyr’s issue. We only need to add two function(a, b) { return (a % +b + +b) % b; } That makes @xixixao therefore |
You only need the second
coffee> '1' + '42'
'142' The lesson being you should really not use strings instead of numbers. |
Conflicts: lib/coffee-script/grammar.js lib/coffee-script/lexer.js lib/coffee-script/nodes.js lib/coffee-script/parser.js test/regexps.coffee
@epidemian: Yes, please. |
Done. (555 tests!) |
check = (a, b, expected) -> | ||
res = a %% b | ||
# Don't use eq because it treats 0 as different to -0. | ||
ok res == expected or isNaN(res) and isNaN(expected), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use ==
either. Why can't we distinguish 0
from -0
in the tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
==
is the same as is
, let's go with the latter :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michaelficarra, yeah, the only curious case is 0 %% -1
, which evaluates to -0
(which makes sense since the result of the modulus should have the same sign as the divisor), so we could assert that in the tests and be explicit 👍
@xixixao oops, all other mathematical operators except |
Add new mathematical operators
This is an attempt to (finally) include a new set of mathematical operators beyond what JavaScript provides.
Included on this PR are the operators that seemed the most consensually accepted by the community and that seemed the most practical and intuitive in terms of syntax to me:
**
,//
and%%
.Other operators might be useful, but i think it's important not to over do it and try to convert every function on
Math
into a new operator. So i deliberately left out more sophisticated operators like LiveScript's chainable maximum and minimum operators (<?
and>?
) or a unicode√
operator for square roots 😸That being said, this is an open pull request, so if you think other operators deserve to make it in (or some of these doesn't deserve it), please comment.
Small caveat
The floor division operator token clashes with the current empty regex token:
//
. I think there's no way to have them both; even if we did some grotesque lexer + parser magic introducing some ambiguity in the lexer (say, a FLOOR_DIV_OR_EMPTY_REGEX token) and then trying to disambiguate it in the parser, there are ugly corner cases, likea //i
, which is valid on the current master as a call toa
with an empty regex and is also valid in this PR as a floor division betweena
andi
.I opted to remove the support for empty regexes in this PR (notice a missing regex test), but i don't know if this is an acceptable removal. I would expect that nobody would ever use an empty regex, as i see no point in doing so, but i've been very mistaken about guessing what other people don't do on their code in the past. So, if we decide to remove the empty regex token, could we make an intermediate minor release that marks that token as a syntax error and some time after that release these new set of operators?