Steps of Scala: monads, schmonads

by lnc on July 29, 2011

The reason everyone talks about monads is cause they are indeed important, and very useful. The reason you don’t understand most of the explanations you come across, quality as they are, is, they start at the far end – with definitions and concepts. We’ll try starting the other end, just for kix – with simple examples.

Forget all about “monads” for a second, and let’s talk about Option[A]. It’s a simple thing meant to save us from NullPointerExceptions, and it very well does. Like Schrödinger’s cat, it’s either Some() or None, but never null. It’s also a bit baffling at first, isn’t it?

Say you have an Option[Int] returned to you from a function that parses a String into Int. If the integer is there, you’ll do something with it; if not, then not. The first instinct of a Java/Ruby programmer? Unwrap the damned thing – it’s just a container:

val x = option.get

All good, except it will throw an exception if the option is a None. Crap. Ok, there’s that hot and functional pattern matching thing:

option match {
case Some(x) => doUsefulStuff(x)
case None => doodle()
}

Say, how much better is this compared to an if/else against null? Well, not much. So what’s all this Option stuff about, then? Here’s what:

option.map{doUsefulStuff(_)}

You know what this does – calls doUsefulStuff on the content of the Option, and return the result wrapped in another Option. If there was nothing inside the Option, it does nothing. Easy to understand, but still weird – why is it useful? Because you can safely chain it endlessly:

option.
  map{findUserById(_)}.
  map{reportOnUsersBuyingHabits(_)}.
  map{prepareUsersAdvertisingProfile(_)}

You can run your entire computation through this pipeline w/out ever having to worry about a step failing. If it does, you’ll simply get a None at the end. Congratulations, you’re now familiar with the Simplest Useful Monad – the Option type (aka the Maybe type).

Careful with it – it will warp your brain. To quote a more-able man than myself, Mr Daniel Spiewak: “Once you start thinking about structuring your code to use Option in languages which have built-in support for it, you’ll find yourself dreaming about such patterns in other, less fortunate languages. It’s really sort of bizarre how much this little device can open your mind to new possibilities” (from his explanation of the Option.)

Questions about it that are already arising to bother you, such as “how do I know what exactly failed on a given step?” and “what if a step itself returns an Option – wouldn’t I end up with an Option[Option[A]], and how deep does that rabbit hole go?” – they will all be answered shortly.

Stay tuned! :)

{ 1 comments }

Intro

A proper discussion of a type system requires more background in abstract subjects – algebras, sets, categories, types – than people generally seem to realize. We’re not gonna require such a background (defined, for the sake of argument, as having at least worked through Benjamin Pierce’s “Types and Progamming Languages“.) We’re going to give just enough to get us started reading and writing Scala.

Static Type System

First off, if you’re coming from Ruby, Scala’s type system is unlike what you’re used to – it’s static. Types of all things are known at the moment of their creation, and never change. Types themselves also never change – you can’t re-open a class and add things.

No, this isn’t going to make development any slower or uglier – that’s a flawed notion built on exposure only to Java’s weak and wordy static type system. Languages with powerful, eloquent static type systems pre-date both Java and Ruby.

Type checking is an emergent property of software – it’s always present in some form. If the language doesn’t provide it, you end up having to. The test battery of a medium-size Rails project usually includes what amounts to an ad hoc heuristic type checker.

Static type checking by the compiler is a good thing – it catches large classes of errors and allows for type-specific optimizations, all with the use of exceptionally well-tested code. The language just has to try and keep the keystroke price of static types low. Scala makes huge strides that way by using type inference and implicit type conversions.

Type inference

The compiler will always try to figure out the type of an expression, value, variable or method. It will almost always succeed, which means you will rarely have to declare the type of a value, or the return type of a method. The cases where you will have to fall under:

  1. you’re writing a public API
  2. you want to force a type check (useful sometimes)
  3. you’re dealing with one of the few cases where Scala’s type inferencer isn’t (yet) powerful enough

Regarding #3, there are languages powerful enough to be able to always infer types (e.g., Haskell). Scala isn’t one of them, and due to its feature set is unlikely to ever be. However, its type inference has been steadily improving with every release, and will likely improve further.

Implicit type conversions and “Pimp My Library”

Implicit type conversion is a simple idea with powerful repercussions. Scala lets you define methods that convert an instance of one type into an instance of another – say, a java.lang.String into your.own.SuperString. When such a method is in scope (properly imported), you can use a java.lang.String anywhere you need to use your.own.SuperString, and the compiler will automatically call the converter method. Here’s an example adapted from the venerable library Scalaz:

class BooleanW(isTrue: Boolean)
object BooleanW{
  implicit def BooleanTo(b: Boolean): BooleanW = new BooleanW(b)
  implicit def BooleanFrom(b: BooleanW): Boolean = b.isTrue
}

If, like above, you define conversions both to and from the new class, you essentially get the ability to extend a class or trait and use the extension in all existing APIs – just like Ruby’s open classes, but with static type checking. This pattern is so useful that it has its own name – “Pimp My Library.” It’s pervasive in Scala – you’ll see and use it a lot. The specific additions to existing types are usually described as “pimps.”

A little terminology: Algebraic Data Types, Higher-Kinded Types and Type Constructors

Public discussions of Scala often mention formal concepts that underly Scala’s type system’s features. As promised, we’re not going to treat them formally here. Unless you have a math background, starting with the abstract is not likely to be helpful. Instead we’ll match formal terminology to its representation in Scala so you can start building your own notions and learning to feel comfortable with the formalisms.

Algebraic Data Types are expressed in Scala as hierarchies of case classes. Here’s an algebraic data type AreaShape adapted from the Lift web framework:

  trait AreaShape
  case class RectShape(left: Int, top: Int, right: Int, bottom: Int) extends AreaShape
  case class CircleShape(centerX: Int, centerY: Int, radius: Int) extends AreaShape

Higher-Kinded Types are types that take type parameters. You will use them every day: List and Map are both higher-kinded types. The term “kind” refers to how many type parameters need to be resolved before the type becomes concrete. When kinds are discussed, they are sometimes expressed using stars for parameters. For examples, List is of the kind * -> *, which means it needs one type parameter to become a concrete type. Map is of the kind *, * -> *.

Type Constructors, for now, can be considered an alias for Higher-Kinded Types. They are not, but diving into that rabbit hole right now wouldn’t serve us.

Coming up next

Next we will address the things that will affect your Scala code the most – higher-order functions and function invocation syntax.

{ 1 comments }

Steps of Scala: objects and static imports

June 24, 2011

Intro This post series is a way for me to retroactively document my learning of Scala. As language acquisition tools go, consider it a phrasebook. I find I learn new languages best by stealing the habits of others: here’s how you do this, here’s how you don’t do this, here’s why this does or doesn’t [...]

Read the full article →