Introduction to Scala for Rubyists

Matthew Powers
5 min readJun 12, 2017

This blog provides Rubyists with an easy introduction to the Scala programming language. It will show you how to write Ruby-like code in Scala. As you get more familiar with Scala, you can slowly transition from the Ruby way to the Scala way.

Learning a new language is psychologically painful. Learning Scala the Ruby way limits the pain of the transition — let’s dive in!

Variable assignment

Ruby is a dynamically typed language, so variables can be assigned to an object of one type, and then reassigned to an object of a different type.

x = 99
x = "cool"
x = ["one", "two"]

Scala is statically typed so once a variable is assigned, it can only be reassigned to other variables of that type.

var coolNumber = 8
coolNumber = 10 // this works
coolNumber = "phil" // error: type mismatch

Scala also supports immutable references, which are variables that cannot be reassigned, even to other objects of the same type. The val keyword is used to make immutable references.

val myName = "matthew"
myName = "mateo" // error: reassignment to val

Scala variables with the Any type can be reassigned to values of any type and behave a lot like Ruby variables.

var zz: Any = 66
zz = List(1, 10)
zz = "crazytown"

Experienced Scala programmers don’t use var and Any in this manner, but it’s fine when you’re just getting started!

Arrays

Ruby is dynamically typed, so arrays can contain different types of objects.

random_stuff = ["cat", {}, 77]

Scala has several types of Array-like data structures. We’ll only talk about the List data structure in this article. Other Scala guides would take this opportunity to go off on a huge tangent on the different types of arrays in Scala, but we’ll refrain — you’re welcome ;)

Scala is statically typed, so lists generally need to contain objects of the same type.

val negNumbers = List(-10, -20) // this works

We can use the Any workaround again to create lists with different types of objects.

val weirdStuff: List[Any] = List("blade", 99, "glitter")

Array mutability

Ruby arrays are mutable, so elements can be added or removed.

best_friends = ["toby", "jim"]
best_friends.push("stanley")
best_friends.shift

Scala lists aren’t mutable, but you can create a new list by unioning two lists.

val englishNames = List("matthew", "powers")
val colombiaNames = List("mateo", "poderes")
val allNames = englishNames ++ colombiaNames
allNames // List("matthew", "powers", "mateo", "poderes")

Scala has mutable array data structures, but we’re not going to dig into those right now. We’re avoiding the Scala rabbit holes.

Collection Methods

Ruby borrowed methods like map, filter, and inject from functional programming languages to make it easier to work with collections.

words = ["cAt", "sEAt", "cRAzY"]
words.map { |w| w.downcase } // ["cat", "seat", "crazy"]

Ruby also provides symbol to proc syntactic sugar to allow for a more concise syntax.

words.map(&:downcase) // ["cat", "seat", "crazy"]

Scala also provides a map method for lists.

val movies = List("thOR", "UP", "HeR")
movies.map((movie: String) => movie.toLowerCase())

Scala provides some syntactic sugar with the underscore character.

movies.map(_.toLowerCase())

Classes

Typical Ruby classes use mutable instance variables to maintain state.

class Person
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def full_name
"#{first_name} #{last_name}"
end
end
bob = Person.new("bob", "loblaw")
bob.full_name() # "bob loblaw"
# Instance variables allow the object to be mutated.
bob.last_name = "barker"
bob.full_name # "bob barker"

Let’s create a similar Scala class that is instantiated with variables and creates mutable objects.

class Person(var firstName: String, var lastName: String) {
def fullName(): String = {
s”$firstName $lastName”
}
}
val phil = new Person("phil", "helmuth")
phil.fullName() // "phil helmuth"
// the phil object is mutable
phil.lastName = "mickelson"
phil.fullName() // "phil mickelson"

Scala programmers prefer immutable objects, but it’s fine to write code like this when you’re getting started.

Singleton methods and companion objects

Ruby classes can define both singleton methods and instance methods.

class Nintendo  # singleton method
def self.about
"we make fun games"
end
# instance method
def make_something
"we’re building"
end
end# call the singleton method
Nintendo.about # "we make fun games"
# call the instance method
n = Nintendo.new
n.make_something # "we’re building"

Scala companion objects allow for behavior that’s similar to singleton methods.

class Nintendo {
def makeSomething(): String = {
"we’re building"
}
}
object Nintendo {
def about(): String = {
"we make fun games"
}
}
Nintendo.about() // "we make fun games"val n = new Nintendo
n.makeSomething() // "we’re building"

The class Nintendo and object Nintendo definitions must be in the same file for the Scala compiler to interpret object Nintendo as a companion object. Otherwise, the compiler will interpret the object definition as a namespace collision.

Modules and Traits

Ruby uses modules to add instance methods to a class.

module CatEnthusiast
def cat_feelings
"I LOVE CATS!"
end
end
class Student
include CatEnthusiast
def hobby
"sleep"
end
end
s = Student.new
s.cat_feelings # "I LOVE CATS!"

Traits in Scala are similar to modules in Ruby.

trait CatEnthusiast {
def catFeelings(): String = {
"I LOVE CATS!"
}
}
class Student extends CatEnthusiast {
def hobby(): String = {
"sleep"
}
}
val s = new Student
s.catFeelings() // "I LOVE CATS!"

Nesting modules vs. packages

Ruby classes are organized in a nested module structure that mimics the directory structure.

For example, the lib/turf/lookup.rb file looks like this in the Turf open source project:

module Turf 
class Lookup
## code here
end
end

Scala classes are located in a nested package structure that follows the directory structure.

Here’s what the src/main/scala/com/github/mrpowers/spark/daria/sql/SparkSessionExt.scala file looks like in the spark-daria open source project.

package com.github.mrpowers.spark.daria.sqlobject SparkSessionExt {
// code goes here
}

Scala follows some crazy Java directory structure conventions, so code is often nested deeply inside empty directories. If you’re completely new to the Java / Scala ecosystem, the conventional directory structure will seem ridiculous. Thankfully Scala packages provide a clean way to navigate the maze of empty directories. Deeply nesting Ruby modules gets clunky and Scala’s package notation really shines in comparison.

Monkey patching

Ruby makes it easy to add methods to existing classes with a process called monkey patching.

class String
def funny_joke
"Good luck debugging this code!"
end
end
"hi".funny_joke # "Good luck debugging this code!"

Scala allows for monkey patching with implicit classes.

object StringExt {
implicit class StringMethods(s: String) {
def stillUgly(): String = {
"Yuck, except awesome sometimes"
}
}
}
import StringExt._
"hello".stillUgly() // "Yuck, except awesome sometimes"

Next Steps

Jason Swartz wrote an awesome Scala book that’s approachable for Ruby developers.

I’ll write another blog post covering more advanced language features soon :)

--

--

Matthew Powers

Spark coder, live in Colombia / Brazil / US, love Scala / Python / Ruby, working on empowering Latinos and Latinas in tech