gkz / LiveScript

LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming.
http://livescript.net
MIT License
2.31k stars 156 forks source link

Reintroduce +++ operator to mean concatenation with mutation #1111

Open determin1st opened 3 years ago

determin1st commented 3 years ago

Can you tell why deprecate +++ operator? It doesn't mess with ++ right, must be the same precendence with ++, so could be "recovered" easily? Any story about it maybe :]

my "projected" use-case:

# inside of object constructor function...
# ...
    @item = item = {} # all
    @list = list = [root] # all ordered
    ####
    list +++ (querySelectorChildren box, '.item')
    a = -1
    while ++a < list.length
        if b = querySelectorChild list[a], '.section'
            list +++ (querySelectorChildren b, '.item')
# ...
# end of example
rhendric commented 3 years ago

It's just been renamed to  ++  (note the spaces; unspaced ++ is increment). Your example works if you replace all the +++ with ++= (you need the = because  ++  doesn't mutate the lists, it only returns their concatenation).

determin1st commented 3 years ago

ye, i figured only ++ won't go, so thanks, didn't know about ++=, will check

determin1st commented 3 years ago

one more thing about it (generated from ++=):

      while (++a < list.length) {
        if (b = querySelectorChild(list[a], '.section')) {
          list = list.concat(querySelectorChildren(b, '.item'));
        }

the assignment is not needed, right, should be only list.concat(..)

rhendric commented 3 years ago

No, that's what I was trying to say at the end of my comment:

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

determin1st commented 3 years ago

oh, my, i've missed the bug then..

any chance to resurrect +++ from the imperative hell?

vendethiel commented 3 years ago

+++ did exactly the same as ++ does

determin1st commented 3 years ago

maybe let's tweak it to Array.splice ? so it will mutate the left operand? i mean new +++ with .splice

vendethiel commented 3 years ago

Why? just use ++=

determin1st commented 3 years ago

because +++ is a sister of <<<

with ++= i have to follow this path:

# ...
    item = {} # all
    list = [root] # all ordered
    ####
    list ++= (querySelectorChildren box, '.item')
    a = -1
    while ++a < list.length
        if b = querySelectorChild list[a], '.section'
            list ++= (querySelectorChildren b, '.item')
    # assign one more time :(
    @item = item
    @list = list
# ...

see, i could assign it one-line at the top, have to add 2 more lines. with the .splice as +++ it would be

# ...
    @item = item = {} # all
    @list = list = [root] # all ordered
    ####
    list +++ (querySelectorChildren box, '.item')
    a = -1
    while ++a < list.length
        if b = querySelectorChild list[a], '.section'
            list +++ (querySelectorChildren b, '.item')
# ...

makes sense?

ceremcem commented 3 years ago

@determin1st Why can't you use @list and @item during the operations?

# ...
    @item = {} # all
    @list = [root] # all ordered
    ####
    @list ++= (querySelectorChildren box, '.item')
    a = -1
    while ++a < @list.length
        if b = querySelectorChild @list[a], '.section'
            @list ++= (querySelectorChildren b, '.item')
# ...

What is wrong with above code?

rhendric commented 3 years ago

IMO, if you want to mutate an array by appending new elements, you should just call .push on it. This is subjective, but based on experience with similarly-spelled operators I wouldn't expect an expression spelled a +++ b to mutate a, or even treat a and b fundamentally differently.¹ a <<< b is at least visually asymmetric enough to hint at the side effect. I know LiveScript has a whole mess of operators, but I don't think more operators need to be introduced just as aliases to methods. a.push ...b is much clearer about what's going on, is already familiar to every JavaScript programmer, and is, what, 9 characters instead of 5? Not exactly a huge downside. It doesn't look as mathematical, but since what's happening isn't a mathematical concatenation but rather an impure mutation, I think that's a positive.

  1. In Haskell, which is the only language I know with a +++ operator in the core library, +++ is a way to combine functions²; f +++ g is a function that can take a value tagged Left and apply f to it, or a value tagged Right and apply g to it. Haskell's ++ operator means the same thing as in LiveScript.
  2. Well, arrows, but anyone who needs this explanation probably doesn't need to worry about the difference.
determin1st commented 3 years ago

@ceremcem well ye, i only wanted a bit of perfectionism, that can be done in multiple ways, but somehow +++ appeared as a natural thing when i wrote the initializer's code.

@rhendric 9 characters plus engine implementation dependent, can't say how much arguments allowed to push(...b), i mean just a loop would be more solid and maybe faster

rhendric commented 3 years ago

Which implementations don't support push with multiple arguments? It's in the ES3 spec, which is the version of JS that LiveScript nominally targets as a baseline when not using features that more or less explicitly require more modern engines, like generators and promises.

determin1st commented 3 years ago

i mean here, the "Merging two arrays" section: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push

says:

Do not use this method if the second array (moreVegs in the example) is very large because the maximum number of parameters that one function can take is limited in practice. See apply() for more details.

i checked the limit was 65535 for the second array in Chromium. Sure, it's hard to reach but still..

rhendric commented 3 years ago

.splice is presumably subject to the same limitation, right? So if this matters to you, the only viable approach would be to use (either manually, or have some syntax compile into) a loop. But I'm guessing that for such large arrays, .concat would be faster than a loop, so the best thing for large arrays would be ++= and the best thing for small arrays would be .push .... Since PureScript can't know how big your arrays are, the programmer has to select the best approach. So I think you would need to make the case that whatever +++ would compile to is better than .push ... for small arrays, or better than ++= for large arrays. What do you think about that?

determin1st commented 3 years ago

i thought that .splice takes array as an argument first, it doesn't, so it doesn't fit.

i've made some tests: http://jsbench.github.io/#482b62ecdddfeffcea89d30ded47adb6

so, by the number of elements (in the second array):

20: loop is 18% faster 200: push is 38% faster 2000: push is 36% faster 20000: push is 50% faster 200000: push is dead, maximum call stack exceeded

.push is becoming faster starting from 60 elements in the second array .concat is becoming faster starting from 50 elements in the second array

so, for small arrays <60 elements and also for super huge arrays the loop is better

i doubt that speed should be the driving factor of +++, the main point (feature) is the mutation, right