Hacker News new | ask | show | jobs
by dyscrete 4996 days ago
It's a great idea, but it seems ridiculous how even simple arithmetic gets translated to an unreadable mess of javascript.

puts 4 + 2 + 5 + (19 + 3 * 4) - 8 / 10

translates to:

(function() { var __opal = Opal, self = __opal.top, __scope = __opal, nil = __opal.nil, __breaker = __opal.breaker, __slice = __opal.slice; var __a, __b, __c, __d, __e, __f, __g, __h; return self.$puts((__a = (__c = (__e = (__g = 4, __h = 2, typeof(__g) === 'number' ? __g + __h : __g['$+'](__h)), __f = 5, typeof(__e) === 'number' ? __e + __f : __e['$+'](__f)), __d = (__e = 19, __f = (__g = 3, __h = 4, typeof(__g) === 'number' ? __g * __h : __g['$*'](__h)), typeof(__e) === 'number' ? __e + __f : __e['$+'](__f)), typeof(__c) === 'number' ? __c + __d : __c['$+'](__d)), __b = (__c = 8, __d = 10, typeof(__c) === 'number' ? __c / __d : __c['$/'](__d)), typeof(__a) === 'number' ? __a - __b : __a['$-'](__b))) })();

4 comments

It's really not terribly complicated to figure out what it does, here's the formatted version for adding two numbers

    (function() {
          var __opal = Opal, self = __opal.top, __scope = __opal, nil = __opal.nil, __breaker = __opal.breaker, __slice = __opal.slice;
          var __a, __b;
          return self.$puts((__a = 1, __b = 2, typeof(__a) === 'number' ? __a + __b : __a['$+'](__b)))
    })();
The first line just declares some Opal-specific variables (__scope, nil, __breaker, etc.), which I guess are created whether or not they're actually used. The second line defines the temporary variables used to store the numbers. Then, depending on the type, it either actually adds them or use its own '$+' function, which I guess it adds to the prototype for all objects.

In terms of having to ever debug something like that, I agree that it'd probably be a huge pain.

I agree that it's reasonable. Consider this C program:

  int main()
  {
    return 4 + 2 + 5 + (19 + 3 * 4) - 8 / 10;
  }
Becomes the following assembly:

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
  _main:
  Leh_func_begin1:
    pushq   %rbp
  Ltmp0:
    movq    %rsp, %rbp
  Ltmp1:
    movl    $42, -8(%rbp)
    movl    -8(%rbp), %eax
    movl    %eax, -4(%rbp)
    movl    -4(%rbp), %eax
    popq    %rbp
    ret
  Leh_func_end1:
Yes, that's going from "high level" to "low level," but much of the same concepts exist. You have to set up a bunch of stuff unrelated to the computation first, which in the Ruby-to-JavaScript case means setting up the runtime system. In the C-to-assembly case, it means mucking around with the stack. Then the actual computation may not be the most optimal thing, because it was generated by a general framework which can handle any arbitrary computation. Reducing it to something more reasonable looking is an optimization.
In Ruby 2100 is an exact integer. In JavaScript, it is promoted to a poor floating point approximation. If you want to try to preserve Ruby's numeric semantics, you can't use native math.
I think IEEE doubles can represent up to 53 bit integers exactly. (Someone here will know if that's true.)
Gah, the website ruined what I was typing. I meant 2 to the power of 100. Which is a 100 bit integer, and certainly not correctly calculated in JavaScript.
While your general point is correct, the details aren't. Powers of 2 are represented exactly in floating point (for any number, only the 53 most significant bits can be stored). So 2^100 + 1 is an example of something that's non-representable in JS.
We could have a philosophical debate about what number a finite precision representation represents when you exceed its precision. But on a practical level, if you print it, what do you get? An exact integer, or a floating point representation?
It makes sense, considering JS's silly coercion rules and overflow, which don't match Ruby's.
the problem is that ruby lets you redefine + anywhere you like, and return values of any type. so you need the most general translation of that expression into a series of method dispatches.