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.sql object 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 :)