The Either Monad
The past week has been pretty quiet. Without a looming CPSC 509 homework deadline, I’ve found that school is somewhat relaxed. I’ll probably regret saying that once project deadlines start to pick up. I’ll take a break from sprouting what I’ve learned in the past week and instead talk a bit about the Either monad in Scala.
Exceptions as they’re presented in most languages in common use today represent some kind of exceptional program state. This can be a user input error, or maybe a file system corruption. They’re usually handled using a try-catch
block, like something below in Java
try { // might throw an exception if there are two transactions occurring at once this.performTransaction(); } catch (Exception e) { // do something to handle it }
or in everyone’s favourite language, Python
try: self.performTransaction() except Error: # do something to handle it
This is pretty standard stuff. That being said, I’d say that it doesn’t really lend itself well to the style of functional composition that’s becoming more prevalent these days, especially with respect to functional programming languages. Here’s an alternative.
Enter Either
I’ll use Scala for this example (since I’m pretty familiar with it), but there are analogues in other languages that I’ll explain later. Here we go:
sealed abstract class Either[+A, +B] { ... } final case class Left[+A](a: A) extends Either[A, B] { ... } final case class Right[+B](b: B) extends Either[A, B] { ... }
This might seem like a bunch of gibberish (it did to me when I first saw it), and there’s a lot to unpack. The Either
abstract class is just way of representing two possible values. This is what Either[+A, +B]
means. The valueA
represents one type of the Either
(the error type by convention), and the value B
represents the other (the value upon success). The +
signs imply contravariance, which means that if you have something like Either[Foo, Bar]
, it preserves all the subtyping relations from a generic Either[+A, +B]
The Left[+A](a: A)
represents one of the possible values that the Either
can take on. By convention, it’s the error value. So we can use it like this
// Resolve with an error if n is less than 10 def toString(n: Int): Either[Error, String] = if (n < 10) { val err = Error(s"n was less than 10") Left(err) } else { Right(n.toString) }
conveniently, this shows us how Right[+B](b: B)
is used. By convention, the correct or the “right” value is wrapped by the Right
constructor. So why is this cool?
Well, to a normal person, it probably isn’t very cool. But I never claimed I was normal so this is incredibly cool to me. The title of this newsletter was the Either Monad. This means that all our fun monadic operations (I’m sure there’s a correct word for what I’m trying to say here) can be called on the Either
type. So the following is possible
toString(9) .map(strRep => Integer.parseInt(strRep)) .filter(_ == 9)
Notice here that the .map
is right-biased. This means that it will transform the value of the Either
if it’s a Right
, or otherwise leave it as is. We’ve effectively eliminate the need for a try-catch
construct in our language, and even programs which throw exceptions can be composed and pipelined in our usual way.
Next week I suspect I’ll have much more school content to write about, but that’s all for today.