https://github.com/whitequark/coldruby
http://www.ntecs.de/blog/articles/2007/01/08/rubyjs-javascri...
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.
See https://developer.mozilla.org/en-US/docs/JavaScript/Referenc...
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))) })();
(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.
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.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?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?
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.
Why not make a Ruby -> Coffeescript compiler, since they're a lot closer in the first place?
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 :( (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());
})(); 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();
})(); 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
Ruby doesn't have the same semantics as JavaScript, so you can't have a 1-to-1 mapping of language constructs.
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.
If you can use the same code for both client and server, then it opens up the possibility of sending chunks of logic over the wire.
Here's a trivial example: a website uses Markdown for comment formatting. You can improve the feedback cycle, especially for those who aren't overly familiar with Markdown, by rendering a preview on the client side. However, this means that you need two Markdown parsers, one in your server-side language, and one in JavaScript. That's a potential duplication of effort. Someone has already written a JS Markdown parser, but it might not have exactly the same behaviour as your server implementation. So why not just write the code once and have it work on both sides?
I have a feeling that there are a lot more interesting things that you can do with split computation, but I haven't seen many examples yet. I think they'll come.