

Opal - Ruby to Javascript compiler - cap10morgan
http://opalrb.org

======
DanielRibeiro
Cool idea. Not the first attempt though:

<http://hotruby.yukoba.jp/>

<https://github.com/whitequark/coldruby>

<http://rb2js.rubyforge.org/>

[http://www.ntecs.de/blog/articles/2007/01/08/rubyjs-
javascri...](http://www.ntecs.de/blog/articles/2007/01/08/rubyjs-javascript-
no-thank-you/)

<https://github.com/jessesielaff/red>

<http://www.playmycode.com/docs/quby>

<https://github.com/mattknox/8ball>

Unfortunately, none of them seem to be to handle the mor dynamic features of
ruby, like _method_missing_ , which diminishes the possibility of reusing
existing ruby code in the browser.

~~~
pygy_
Javascript Harmony (ES6) Proxies will allow to define getter catchall and
other kinds of traps.

See [https://developer.mozilla.org/en-
US/docs/JavaScript/Referenc...](https://developer.mozilla.org/en-
US/docs/JavaScript/Reference/Global_Objects/Proxy)

~~~
tlack
Minor FYI: for this who want to play with Proxies right now, you can use a
recent-ish copy of node invoked with node --harmony_proxies Enjoy!

~~~
pygy_
Actually, V8 implements an older version of the spec. You can get a polyfill
created by the designer of the Proxy spec here:
<https://github.com/tvcutsem/harmony-reflect>

It requires `node --harmony` (actually only Harmony Proxies and Harmony
WeakMaps, but the rest doesn't hurt.)

------
dyscrete
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))) })();

~~~
Scriptor
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.

~~~
scott_s
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.

------
doktrin
Being relatively new to cross-language compilation, how are one language's
class APIs typically translated into another?

For example :

    
    
       [1,2,3].shuffle 
    

[turns into] =>

    
    
       (function() {
         var __opal = Opal, self = __opal.top, __scope = __opal, nil = __opal.nil, __breaker = __opal.breaker, __slice = __opal.slice;
      
         return [1, 2, 3].$shuffle()
       })();
    

Of course, since JS arrays do not have a "shuffle" method, the output reads :

    
    
       TypeError: Object 1,2,3 has no method '$shuffle'
    

Is the answer to simply use class/object methods that are present in both
languages?

~~~
mossity
Could probably also include something like Sugar.js or Underscore.js that
tries to bring a lot of the missing syntactic sugar to JS.

------
Xcelerate
I really like this idea of compiling code to Javascript. Brenden Eich makes
some good points for it in his series of slides (starting here:
<http://brendaneich.github.com/Strange-Loop-2012/#/21>).

I'm working on a little programming language myself that compiles to
Javascript. In most languages that do this, I wish there was better
information on the mapping between the domain language and its Javascript
codomain. I think this would be a good open source project idea: a tool to
assist in the compilation of various languages to Javascript. Something to the
effect of how source lines in C can be printed above their generated assembly.
Anyone want to give it a shot?

~~~
slurgfest
Why is it not possible to engineer a basic VM to do this job from the start,
rather than retrofitting Javascript to act as a bytecode?

~~~
adrusi
implementing a vm in javascript would be noticeably slower than compiling
directly to javascript. Dart has its own vm, which will eventually be bundled
with chrome since it's a google-backed project, but for backwards
compatibility it still needs a dart source to javascript source compiler.

------
jhrobert
They lost me at the "nil" section.

RubyScript is what we need => efficient code, à la coffeescript, with a Ruby
syntax.

There is some impedance mismatch between ruby & javascript semantics. Trying
to hide this fact has a cost in terms of performance.

I would love to have some ruby syntax as long as it does not compromize the
speed of my code.

There is a trend these days where syntax and semantics are becoming
orthogonal, that's nice.

Hence GoScript, PascalScript, PrologScript, CppScript, SmallScript...

Thanks to sourcemaps the issue of the generated code readability has
disappeared. Only the performance matters from now on.

~~~
SilasX
>RubyScript is what we need => efficient code, à la coffeescript, with a Ruby
syntax.

Why not make a Ruby -> Coffeescript compiler, since they're a lot closer in
the first place?

------
vjeux
Unfortunately their hash table uses Javascript objects. Therefore it does not
behave exactly like Ruby.

Eg:

    
    
      # Opal
      h = Hash.new
      h['0'] = 1 
      h[0] = 2
      print h
      # {"0"=>2}
    
      # Ruby
      h = Hash.new
      h['0'] = 1 
      h[0] = 2
      print h
      # {"0"=>1, 0=>2}
    

It is very hard to reproduce those low level specifications without rebuilding
everything from scratch sadly :(

~~~
lucian1900
ECMA5 has real maps, which Opal could use. Some browsers support them already.

------
1qaz2wsx3edc
Yup, that's some good brainfuck:

    
    
          (function() {
            var __opal = Opal, self = __opal.top, __scope = __opal, nil = __opal.nil, __breaker = __opal.breaker, __slice = __opal.slice, __klass = __opal.klass;
            var adam = nil, __a, __b;
            (__b = [1, 2, 3, 4], __b.$each._p = (__a = function(a) {
    
        
              if (a == null) a = nil;
    
              return this.$puts(a)
            }, __a._s = self, __a), __b.$each());
            (function(__base, __super){
              // line 5, (file), class Foo
              function Foo() {};
              Foo = __klass(__base, __super, "Foo", Foo);
    
              var Foo_prototype = Foo.prototype, __scope = Foo._scope;
    
              return Foo_prototype.$name = function() {
          
                if (this.name == null) this.name = nil;
    
                return this.name
              }, 
              Foo_prototype['$name='] = function(val) {
          
                return this.name = val
              }, nil
            })(self, null);
            adam = __scope.Foo.$new();
            adam['$name=']("Adam Beynon");
            return self.$puts(adam.$name());
          })();

~~~
ghayes
If you look at this rationally, it's actually pretty sane. It's just very
simple JavaScript OOP inline with translated code. Even more beautiful is the
output of modules:

    
    
         module Run
           def run
             puts "I am running!"
           end
         end
    
         module Jump
           def jump
             puts "I am jumping!"
           end
         end
    
         class Foo
           include Run
           include Jump
         end
    
         mario = Foo.new
         mario.run
         mario.jump
    
    

translates to:

    
    
         (function() {
           var __opal = Opal, self = __opal.top, __scope = __opal, nil = __opal.nil, __breaker = __opal.breaker, __slice = __opal.slice, __module = __opal.module, __klass = __opal.klass;
      
         	// Define Mario
         	var mario = nil;
      
         	// Javascript "Run" class
         	(function(__base){
             // line 2, (file), module Run
             function Run() {};
             Run = __module(__base, "Run", Run);
             var Run_prototype = Run.prototype, __scope = Run._scope;
    
             Run_prototype.$run = function() {
          
               return this.$puts("I am running!");
             }
                 ;Run._donate(["$run"]);
           })(self);
    
         	// Javascript "Jump" class
           (function(__base){
             // line 8, (file), module Jump
             function Jump() {};
             Jump = __module(__base, "Jump", Jump);
             var Jump_prototype = Jump.prototype, __scope = Jump._scope;
    
             Jump_prototype.$jump = function() {
          
               return this.$puts("I am jumping!");
             }
                 ;Jump._donate(["$jump"]);
           })(self);
    
         	// JavaScript "Foo" class
           (function(__base, __super){
             // line 14, (file), class Foo
             function Foo() {};
             Foo = __klass(__base, __super, "Foo", Foo);
    
             var Foo_prototype = Foo.prototype, __scope = Foo._scope;
    
         	 // $include Run / Jump
             Foo.$include(__scope.Run);
             return Foo.$include(__scope.Jump);
           })(self, null);
    
         	// The ruby executing code translation
           mario = __scope.Foo.$new();
           mario.$run();
           return mario.$jump();
         })();

~~~
sic1
While we are posting contrived examples. What was ever wrong with this?

    
    
        var Foo = function() {};
    
        Foo.prototype.jump = function() {
            return "I jump."
        };
    
        Foo.prototype.run = function() {
            return "I run."
        };
    
        var mario = new Foo();
        mario.run();
        mario.jump();
    

I can't imagine that opal code running nearly as fast as javascript. And if we
are waving around compiled js - coffeescript does a much better job of being
readable. You just black-boxed the whole thing. I like to know what is
happening with my js.

Cool for the rubyist in you i guess. I just kinda go o_0

------
jonny_eh
While very impressive it makes me nervous. The JS it outputs just in the basic
tutorial looks extremely complex, I'd hate to debug that code! Compare with
CoffeeScript, where the output is quite easy to follow, and therefore debug.

~~~
wtetzner
The difference is that CoffeeScript was designed to compile to JavaScript, so
it can adopt JavaScript semantics.

Ruby doesn't have the same semantics as JavaScript, so you can't have a 1-to-1
mapping of language constructs.

~~~
jonny_eh
Oh I get that. I'm sure they did as good as job as possible, but it still
makes me nervous.

As nice a language as Ruby is, I don't think it's worth the trade-off. I'll
stick with CoffeeScript since it's a nice compromise between Ruby and
JavaScript.

------
lexy0202
Can you interact with the dom inside opal?

~~~
marcamillion
Seems so: > Document['.post .title'].html = @post[:title] Document['.post
.body'].html = @post[:body] Document['.post .comments'].html = comments_html

From <https://github.com/opal/opal-rails#readme>

------
sambuna
I certainly see future for this!

------
jblock
Source maps? Source maps.

------
5vforest
What the use case for something like this? Am I missing something?

~~~
Sanddancer
Using the language you're comfortable with client-side. Though what this
really shows, once again, is that we really need a good, open, virtual machine
specification for client-side byte code so we aren't stuck with the same
javascript hammer for all problems.

~~~
MatthewPhillips
? Doesn't this show that we _aren't_ stuck with the same hammer? Opal is a
different hammer.

~~~
rrouse
Not really. You're still using Javascript in the end.

~~~
ville
How is that a bad thing? Source code VMs are easier to secure and as the
tooling improves, you don't need to look a the generated JS anymore to debug,
it becomes "byte code".

------
goggles99
Can't those standards committees just do something right for once and create a
standard for a vm that will run in the browser (a la Dartium)? Stop this
monopoly and oppression of innovation!!!

