How to read “difficult” Scala method type signatures

When I first saw Scala generic type and multiple-parameter group code like this ~12 years ago, my initial thought was, “Wow, maybe I need to think about a different career”:

def race[A, B](lh: IO[A], rh: IO[B])(implicit cs: ContextShift[IO]): IO[Either[A, B]]

But in the end, as Rocky once said, it ain’t so bad. :)

Rocky: You ain’t so bad

(Image courtesy of the movie, Rocky III.)

In the following sections I’ll show how I break method signatures like this down in my mind these days.

1) See the different groups

First, train yourself to see the different groups in the Scala method signature. Here I separate the groups slightly and color-code them:

Scala method type signature, Example 2

(Maybe an IDE can do this for you? If not, it should!)

2) `def` means you’re about to see a method definition

Next, start looking at each group, one at a time. In the first group, def says, “You’re about to see a method definition”:

Scala method type signature, Example 3

3) And then the method name

Next, race is the name of the method:

Scala method type signature, Example 4

(Note that this is a method that “races” two calculations, and returns the winner of that race.)

4) Warning, generic types ahead

In the third group, [A, B] is the Scala way to say, “In the rest of this type signature, you’re going to see two generic types. We use A and B because the types may be different”:

Scala method type signature, Example 5

In some cases the types that are supplied may be the same, but because they can be different, we use A and B to note that they can be different.

Also, if we had one type we’d write [A], and if there are three types we’d write [A, B, C], etc.

(When generic types cause my brain problems, I replace them with something simpler, like Int and String.)

5) The first of two parameter groups

Scala lets you have two parameter groups, and the green group shows two parameters named lh and rh:

Scala method type signature, Example 6

If the name IO or IO[A] causes your brain pain, it can help to temporarily think of the types as  being Option[Int] and Option[String], or maybe List[Int] and List[String]; anything that’s easier to grok at the moment.

6) The second parameter group (and an implicit)

The second parameter group is shown in blue. This one says there is a parameter named cs that has the type ContextShift[IO]. The parameter is implicit, meaning a called can provide it explicitly, or not:

Scala method type signature, Example 7

In regard to these parameter groups, the implicit in Group 2 says, “You can call race in two different ways in your code”:

// pass the `cs` param explicitly
val result = race(lh, rh)(cs)

// `cs` must be in scope and is pulled in automatically
val result = race(lh, rh)

7) Lastly, the method return type

The race method returns an IO[Either[A, B]]:

Scala method type signature, Example 8

This is just an Either[A, B] that’s wrapped in an IO for safe keeping.

Either is a construct that returns one of two possible types, so in your code you just need to unwrap the IO to get the Either contents. This is like getting a gift that’s in a box, and when you open the first box you find a second box inside, and then your gift is inside that second box.

Think of IO as being an effect or action

In this particular example, IO is a little outside the scope of this discussion, but if it helps, you can think of IO as being the holder/result of an effect or action. When running effects, libraries like Cats use the term also use the term “unsafe.” Therefore, it may help to think of synonyms for IO, like these:

Effect[Either[A, B]]

EffectHolder[Either[A, B]]

EffectResult[Either[A, B]]

ActionResult[Either[A, B]]

ResultOfAnAction[Either[A, B]]

Unsafe[Either[A, B]]

For more on IO, and Either, see my articles:

Summary: Seeing the entire “race” method signature

As I mentioned before, this is a method that “races” two effects, and returns the winner of that race. Hopefully now you can see that all of this is expressed in the method’s type signature. Everything the method needs goes into its two parameter groups, and its result is contained inside the IO:

Scala method type signature, Example 2

And finally, if method signatures like these have made you think about a career change, I hope this helps. :)