Some may say Ruby is a bad rip-off of Lisp or Smalltalk, and I admit that. But it is nicer to ordinary people.
When I heard the Smalltalk traits of Ruby, I was intrigued. When I learned more, I enjoyed Ruby’s similarities with one of the most beautiful and powerful languages I’ve known. As I dug deeper, I enjoyed more of its wonderful metaprogramming abilities, which makes Ruby’s classes a lot more dynamic and easy to declare than Smalltalk’s. After reading this in-depth comparison of both, and Kent Beck’s article on the incompatibilities of Smalltalk’s VM implementations, I realized that I was generally more in favor of Ruby than Smalltalk (even though I fear that What Killed Smalltalk Could Kill Ruby, Too).
But Ruby is not perfect. In all fairness, its creator claims that it’s just plain impossible to design a perfect language. But I do believe it could be a bit better. People do point out some controversial rough edges, but these seem a bit trifle when compared to what really bothers me:
- No scoped open classes. It is an issue that is actually being considered to be solved on 2.0 (and there are some branches of ruby that enable it), but, for the time being, there is no way to make the changes made by opening an existing class only apply to objects while being used on the a lexical scope. This is not the same as adding and removing the changes as, in this case, calls that fall out of scope (such as to another module or file) still see the changes. It would be nice to have both ways of open classes: scoped and not scoped.
- Method arguments do not interleave with the method’s name (like in Smalltalk). Example: Instead of calling: File.fnmatch(‘*’, ‘/’, File::FNM_PATHNAME) you’d be to call it like: File.fn match: ‘*’ path: ‘/’ flags: File::FNM_PATHNAME. This seems weird, but it is a very powerful feature that allows method invocation to be descriptive, similar to Python’s named arguments or even Ruby’s named arguments with a hash. On the other hand it has a cleaner syntax than the former, and does not require checking hash keys as the later (the later is still useful for methods that want to receive an arbitrary list of named arguments). It would change a bit of the syntax of method_missing and how to deal with varargs and blocks for each parameter, but these can be dealt with as Scala does with its parameter lists.
- There is no method called (). Procs (who are the obvious beneficiaries of such change ) use the method call, but one could be an alias to the other. There would be a mild ambiguity here, when you call the function that was just returned. For instance, imagine func is a method that receives no arguments and returns a function (that is: an object that implements the () method) wich also receives no arguments. Then func() could mean 1. call func, and 2. call the function returned by func. I am aware this is kinda of a sensitive topic, but the Scala approach to this issue is very simple: func and func() are the same (provided func can be called with no parenthesis). If you want to call the returned function in the same expression, use func()(). With the alias, you’d be able to call it like func.call() , func().call() or even func.call
- No way to create simple blocks. It would be nice to have something similar to Scala’s underscore or Groovy’s it, which allows method invocations like: collection.map(get_square_of(_)) in Scala and collection.map(get_square_of(it)) in Groovy. Ruby’s symbol coercion to proc (&:method) does not really work on anything besides methods of the arguments of the block.
- Difficulties of composing callback methods. You could be sure to always invoke super on them, and even meta-program all the classes/objects that do not do such thing. However, it is not easy to actually see which methods will be called, or even manipulate/re-prioritize the blocks of code on runtime (kinda like a Chain of Responsibility), which can be very bad, as these methods can modify a lot of behaviour throughout the Object Space.
- The reflection API could be more thorough. For instance, you can’t get the source code of a method/class, etc (as you can in Python). You can use Parse Tree and Ruby2Ruby to do it, but Parse Tree is not portable (does not even work on ruby 1.9) and the output can be formated differently than the actual source code (which can be critical on DSLs). Also, methods added do not have information on which line of code they were added (which is less important when adding methods the recommended way: extending/including Modules), and properties created with class methods (such as those created by attr_reader, or some other libraries equivalents) can’t be discovered on runtime (they are like any other method, with no other meta-data whatsoever). Ruby also seems to be missing some helper methods, such as #metaclass.
- No support for immutability. This is kinda nitpicking, but using recursive freeze (as noted by Dean Wampler) is not really practical (as it is really slow). Neither does it encompass immutable local variables. This is not only useful for concurrency and functional programming issues, but is also useful when writing code that is side-effect free so that it is easier to reason about.
- The return value of a setter method (that is: one that ends with the equals symbol) is the argument, not the return value of the method. This is an issue that matters more when using immutable objects, as the only way for them to “mutate” is to return a new object. Therefore you can’t use a setter method on an immutable object, as, even if it returns a new one, the runtime will ignore the return value and set to the variable the argument that was received. On the other hand, I don’t think this can be changed without breaking a lot of existing code.
Several of this annoyances can be solved with a heavy dose of open classes, s-expressions manipulation (using Parse Tree) and meta-programming in general. Knuth has said that: Language designers also have an obligation to provide languages that encourage good style, since we all know that style is strongly influenced by the language in which it is expressed. Fully agreeing with such Sapir-Whorf-esque sentence, I feel it would be a good thing if the underlying listed solutions were built into the language itself (and supported across implementations, such as JRuby, Iron Ruby, Rubinius, Maglev), as it would not only improve, even if a little bit, the language itself, but also they way its users write code.
Update: Thanks Michael Fellinger for noting that ruby blocks are fully adherent to method definitions on 1.9, as they allow both default parameters and blocks as arguments.
It has been a while since Java was the sole language running over a JVM. Scala is another such language which gained a lot attention recently for being used to scale Twitter‘s backend. Scala differs from most other languages that run on the JVM, such as Groovy, JRuby and Jython, as it is statically typed. This means that, similar to Java and C#, the types must be known at compile time. Scala is usually introduced as being both OO and functional. While this statement is true (and daunting, as many people are uncomfortable with the f*** word), it fails to grasp the important aspects of Scala.
Among the most direct benefits of using Scala feature:
- Compatible with Java. Kinda obvious (as so are all the other 200+ languages over the JVM), but it is such an important feature that should not be overlooked. This means that Scala can use all Java libraries and frameworks. Which shows respect for people’s and companies investment on the technology.
- Joint Compilation. This means that, like Groovy, Scala classes are compiled to Java classes, and therefore can be used on Java projects (even by java classes on the same project they are defined). Even if your team decides to make the complete move towards Scala, this can be useful integrating with dynamic languages via JSR 223.
- Type Inference. If the compiler can guess the type (and it usually can), you don’t have to tell it. This allows Scala code to be as concise as dynamic languages, while still being type safe.
- Implicit conversion allows you to achieve in a type safe way what extension methods do for C# and open classes (mostly) do for ruby. That is: add methods to types you might not have not defined yourself (such as strings, lists, integers). This is one of the features that make Scala DSL friendly.
- Object immutability is encouraged and easy to accomplish. Scala even comes with immutable collections built-in.
- Getters and Setters are automatically generated for you. If you don’t want them (if you only want setters for example), you have to explicitly make them private. Which is not a problem, as the common case is to want them.
- Scala has first-order functions and implements an enumeration protocol (with the iterable trait), which helps keeping code clearer, more concise, and brings several other benefits.
- The Actor programming model eases up the development of highly concurrent applications.
- Exceptions don’t have to be explictly caught or thrown. It can be argued that having checked exceptions does more harm than good.
These features alone would be enough to make Scala a very interesting language, and worth being heralded as the current heir apparent to the Java throne by one of JRuby’s creator, Charles Nutter (a view somewhat shared by Neal Gafter). Or even worth of being endorsed both by Groovy’s creator, James Strachan, and by the inventor of Java, James Gosling. Nonetheless Scala is deep, and there are several exciting advanced features that allow developers to be more productive. But learning such features before getting a good grasp the basics can be quite frustrating, more so without a good supporting literature (such as IBM‘s, Aritma‘s, Jonas Bonér‘s, Daniel Spiewak‘s, Sven Efftinge‘s, the official one, and several others). However it quite is feasible, not only encouraged, to delve into deeper concepts as you need them.
Even though Scala has academic roots (as it shows on its papers page, and some advanced concepts these tackle), Scala has been successfully used on enterprise projects, besides Twitter, such as Siemens, Électricité de France Trading and WattzOn.
Besides all the good points, Scala does have some rough edges. Even though many people are working on overcoming them, they are likely to be relevant on the short term:
- Incipient IDE support. As Lift‘s author expressed, IDEs for Scala, while undergoing a lot of development, are not what they are for Java. There is poor refactoring support, code completion and unit test integration. Not to mention the fact that most framework support tools will not play nicely with Scala. This can also put off some newcomers, as an IDE can help people learn the language. On the other hand, Martin Folwer relativizes this IDE situation, as a language that allows you to be more productive can more than make up for the lack of sophisticated tools.
- Joint Compilation is not supported by most IDEs as well. Again, likely to change as Scala grows in popularity.
- Immutability on a class is not really immutability, since referring objects may not be immutable themselves. And there is no way at the moment to ensure the whole object graph is immutable.
- Making JSR 223 work perfectly with Scala can be challenging. On the other hand, making it work good enough is quite attainable.
- Scala doesn’t support metaprogramming. This can be worked around by combining it with dynamic languages, such as Ruby (following a polyglot programming approach), but if you are going to do heavy use of metaprogramming, than using a whole different language may be a better solution (Fan is another static type language that runs over the JVM, similar to Scala, that has metaprogramming support).
- Frameworks that expect Java source, such as the client-side GWT, will not play nicely with Scala (note that people have made Scala work with GWT on the server-side though). However there is an ongoing project that will translate Scala into Java source.
- The syntax and some concepts are bit different from Java, such as: inverted type declaration order, underscore being used instead of wildcards, asterisks and default values, many kinds of nothing, no static methods (you need to use singleton objects instead) and other minor things. The documentation walks through this quite nicely though, but keep in mind that it is not an automatic transition from writing Java to writing Scala code.
As Joe Armstrong said, the need for languages that allow developers to easily make use of CPUs with multiple cores will only increase as such CPUs become cheaper and gain more and more cores. Scala is quite suited for such task, while Java’s development is stuck dealing with issues that come from being widely deployed, uncertanties of how open it will be in the future and political issues with some of its main contributors. Given the situation, Scala seems to fit quite nicely the role of the successor to Java’s throne.