11 ms·
Opal - Ruby to Javascript compiler
- jonny_eh 14y agoWhile 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 14y agoThe 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 14y agoOh 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.
- denysonique 14y agoOpalScript is going to solve this problem
- 1qaz2wsx3edc 14y agoYup, 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 14y agoIf 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 14y agoWhile 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
- 5vforest 14y agoWhat the use case for something like this? Am I missing something?
- astrodust 14y agoPorting existing code without having to re-implement. Keeping a single reference implementation of a particular method.
- rgovind 14y agoon related note, when and if node.js takes off, does it make sense to have a php-to-javascript compiler so people don't need to redo a lot of stuff?
- mikesea 14y agoRewriting your entire Rails app in Node so that it goes faster!
- Sanddancer 14y agoUsing 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 14y ago? Doesn't this show that we aren't stuck with the same hammer? Opal is a different hammer.
- DanielRibeiro 14y agoCool idea. Not the first attempt though: http://hotruby.yukoba.jp/ http://hotruby.yukoba.jp/ https://github.com/whitequark/coldruby https://github.com/whitequark/coldruby http://rb2js.rubyforge.org/ http://rb2js.rubyforge.org/ http://www.ntecs.de/blog/articles/2007/01/08/rubyjs-javascript-no-thank-you/ http://www.ntecs.de/blog/articles/2007/01/08/rubyjs-javascri... https://github.com/jessesielaff/red https://github.com/jessesielaff/red http://www.playmycode.com/docs/quby http://www.playmycode.com/docs/quby https://github.com/mattknox/8ball 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_ 14y agoJavascript Harmony (ES6) Proxies will allow to define getter catchall and other kinds of traps. See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Proxy https://developer.mozilla.org/en-US/docs/JavaScript/Referenc...
- tlack 14y agoMinor 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_ 14y agoActually, 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 https://github.com/tvcutsem/harmony-reflect It requires `node --harmony` (actually only Harmony Proxies and Harmony WeakMaps, but the rest doesn't hurt.)
- tlrobinson 14y agoWhile I never finished it, my "CappRuby" prototype supported method_missing since it compiled to use the Objective-J runtime. https://github.com/tlrobinson/cappruby https://github.com/tlrobinson/cappruby
- Xcelerate 14y agoI 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 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?
- spacemanaki 14y agoI think what you've described would be mostly accomplished by source maps: http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/ http://www.html5rocks.com/en/tutorials/developertools/source...
- slurgfest 14y agoWhy 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 14y agoimplementing 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.
- dyscrete 14y agoIt'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 14y agoIt'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 14y agoI 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.
- jblock 14y agoSource maps? Source maps.
- lexy0202 14y agoCan you interact with the dom inside opal?
- marcamillion 14y agoSeems 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 https://github.com/opal/opal-rails#readme
- sambuna 14y agoI certainly see future for this!
- doktrin 14y agoBeing 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 14y agoCould probably also include something like Sugar.js or Underscore.js that tries to bring a lot of the missing syntactic sugar to JS.
- vjeux 14y agoUnfortunately 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 14y agoECMA5 has real maps, which Opal could use. Some browsers support them already.
- goggles99 14y agoCan'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!!!
- jhrobert 14y agoThey 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 14y ago>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?