Hi! I’m building an implementation of Ruby in Scala, and I have some questions about the best way to represent Ruby objects and classes using case classes and traits. I had been using classes and inheritance in an older version of this project, but I wanted to challenge myself to do things in a more functional way. In short, I’ve added a trait for each class in the Ruby class hierarchy (BasicObject, Object, String, etc.). These traits are named BasicObjectLike, ObjectLike, etc. and contain the behavior associated with the Ruby classes (i.e. the methods defined on the classes). Currently, these traits inherit from each other as their corresponding Ruby classes do (BasicObject <: Object and so on). I’ve added a case class for each Ruby class that defines its representation and extends its corresponding trait (BasicObjectLike <: BasicObject). I’ve also added a RuntimeContext case class to keep track of instance-specific information (e.g. object ID); each Ruby class case class contains a RuntimeContext.
My questions are:
Is this a fair and idiomatic way to represent Ruby class hierarchies and method overriding, or is there something less convoluted I can do?
Should each Ruby case class contain a RuntimeContext, or should each RuntimeContext contain a Ruby case class? I went with the former primarily because I’d like to be able to call methods directly on the case class instances to make equality less confusing (e.g String#== would depend only on the value of the string itself, but Object#== compares object identity)
If you are so inclined as to look at the source code for these changes, you can find it here (it does not currently compile): https://github.com/ethowitz/sruby/tree/refactor (master contains the old way I was doing things, and refactor has, well…the refactor). You can find the Ruby case classes and traits in src/main/scala/org/sruby/core.
I hope this was comprehensible – thank you in advance!
I think the problem description is too ambiguous for clear guidance.
For instance, it’s not clear if the project is implementing only the Ruby runtime classes or the Ruby language and the base library and the runtime. Heck, you might be even talking about a cross compiler from Ruby to Scala. Unclear…
From “added a trait for each class in the Ruby class hierarchy”. The class hierarchy of a Ruby application can include dynamic extension. I presume this is only referring to a shallow embedding of the core ruby runtime classes. So, I’m guessing:
a ruby language interpreter
runtime in scala
with a shallow embedding of the core runtime classes
Ah, I suppose that information would have been helpful
My intention is to build a complete Ruby implementation, consisting of all of:
Ruby syntax (parser and lexer) – this, along with AST definitions, can be found in src/main/scala/org/sruby/parser and src/main/scala/org/sruby/lexer
The Ruby base libraries (Scala implementations of Ruby classes like BasicObject, Object, Class, String, etc.) – this can be found in src/main/scala/org/sruby/core
The Ruby runtime (AST evaluator) – this can be found in src/main/scala/org/sruby/evaluator
My question refers specifically to the Ruby base library. Eventually, my intention is to allow for the dynamic extension of the base library (and, thus, the creation of new objects at runtime) by creating instances of RubyClass for custom classes and eventually by allowing users to “reopen” classes (or, more generally, add arbitrary methods to objects on the fly).
The primary point of my confusion is how to represent these classes in Scala in a way that naturally mimics/complements their hierarchical structure in Ruby (I accomplished this by using traits, as stated above, but I am not sure that this is the best way ). I hope these clarifications help – let me know if this all makes sense!
That seems like a very ambitious project for an academic exercise. Ruby is free, isn’t it? If I were considering a project of that magnitude, I would lean toward implementing a free version of something that is useful but not free (e.g., Matlab, if you can avoid copyright infringement). Just my two cents worth.
I don’t necessarily intend to make it feature complete, but I think it is a challenge worth undertaking given that:
I find programming language design and implementation interesting;
It has forced me to think a lot about OO vs. functional paradigms given that I am building a “purely” OO language (Ruby) in a language that marries the two paradigms (Scala); and
I write Ruby code for a living, and this is an opportunity for me to dive deep into the semantics of Ruby
Anyway, I’ve already made substantial progress, having already designed the AST and built a parser, lexer, and evaluator, and the master branch can execute basic Ruby programs. I’m just interested in improving my approach.
OK. Given that you are building a ruby implementation in Scala: Consider the requirement of representing the base classes 1 to 1 with Scala classes. This is a shallow embedding of the base classes in Scala. That is not required to build a language implementation. A deep embedding is also possible
If, for instance, you also had the goal of mixing Scala and Ruby code within an application then a shallow embedding would be great.
Consider, then, a deep embedding: The ruby classes are represented by data in Scala. Imagine, for example, having a data type ClassDefinition that would be instantiated with values like name, and fields and methods. All of which are data as well.
Hmmm. I think the path I’ve currently taken is a mixture: using deep embedding to represent classes defined at runtime and shallow embedding to represent classes defined at compile time (the Ruby base classes in this case). It seems to me that having a shallow embedding for the base classes will make it a lot easier to implement things like integer addition: at some point, we will want to use Scala’s Int#+ to add two RubyIntegers.
My original question is aimed more towards how to organize the shallow-embedded classes (the Ruby base classes):
Consider the requirement of representing the base classes 1 to 1 with Scala classes
The core of my question is: should I use Scala classes, case classes, traits, or some combination of these to represent the Ruby base classes? My current approach (quoted from above):
Thank you for taking the time to help me out with this!