Scala for Rubyists

Scala is a language that strikes the sweet spot between succinct, flexible languages like Ruby and verbose, statically-typed languages like Java.

Ruby developers who also have experience in a statically typed language like C++ or Java will have no trouble picking up Scala. You will lose some flexibility (private method? pshh… obj.send(:not_so_private_any_more)), but the cost is not so bad. Scala, though it is a static language, really feels dynamic. If you’ve heard it said that Scala is a scalable language, it’s because you can add features and complexity as you need them but you can dive right in at a very simple level. A script with no type annotations at all will look very similar to its Ruby equivalent.

open classes You can no longer open a class and monkey-patch at will. However, implicits provide the same power (plus type safety!).

# Ruby
class String
  def piratelike
    "Yarg, " + to_s
  end
end

"foo".piratelike # => "Yarg, foo"

The same approach in scala would be to use an implicit conversion.

// Scala
class PirateString(s: String) {
  def piratelike = "Yarg, " + s
}
implicit def string2PirateString(s: String) = new PirateString(s)

"foo".piratelike // => "Yarg, foo"

The advantage that implicit conversions have over monkey-patching is that the added method is only available when the implicit conversion is unambiguously in scope. This means the compiler uses exactly one conversion from the base type to the pimped type, which eliminates the risk you have in Ruby of clobbering other monkey-patches.

traits Traits are very similar to modules in the sense that related functionality can be neatly packaged together and included in classes without the need to subclass.

# Ruby
module EventTracker
  def track(e)
    puts "Recording event '#{e}' at #{Time.now}"
  end
end

class MyApp
  include EventTracker
  def save_game
    # ... save game state
    track :save
  end
end

app = MyApp.new
app.save_game # => Recording event 'save' at ...

Scala’s version:

// Scala
trait EventTracker {
  def track(e: String) {
    println("Recording event '" + e + "' at " + (new java.util.Date(System.currentTimeMillis)))
  }
}

class MyApp extends EventTracker {
  def saveGame() {
    // ... save game state
    track("save")
  }
}

val app = new MyApp
app.saveGame() // Recording event 'save' at ...

objects If you’ve ever used a Ruby module as a singleton, then a Scala object will look very similar.

# Ruby
module StdOutLogger
  def self.info(msg)
    puts "INFO: " + msg
  end
end

StdOutLogger.info("hello world") # => "INFO: hello world"

Scala version is effectively identical:

// Scala
object StdOutLogger {
  def info(msg: String) {
    println("INFO: " + msg)
  }
}

StdOutLogger.info("hello world") // => "INFO: hello world"

map(collect), select, reject, inject/reduce All these traditional functions have their equivalent in Scala. map and collect are the same; select and reject are filter filterNotinject and reduce are foldfoldLeft / foldRight / reduceLeft / reduceRight.

What Else? The compiler will catch a multitude of errors for you that previously required trivial unit tests. This is great.

What do you lose? Really dynamic aspects, like defining new methods at runtime. There used to be no equivalent to method_missing, though there is now an experimental trait Dynamic in 2.9.1 which does the same thing. Ruby libraries tend to be built for developer happiness, but idiomatic Scala libraries are not as wide-spread. This is the blessing and the curse of being a JVM language – Scala can leverage the enormous amount of Java libraries, but at the risk of looking and feeling like Java.