Skip to content

Instantly share code, notes, and snippets.

@rcoh
Last active September 13, 2018 12:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rcoh/4992969 to your computer and use it in GitHub Desktop.
Save rcoh/4992969 to your computer and use it in GitHub Desktop.
Scala patterns for compiler construction
// Useful Scala Patterns for Compiler Design
// 1. Case Classes
// [For full details, see Programming in Scala 2ed, page 310]
// Case classes are syntactic sugar around normal Scala classes. They come prebaked with the following properties:
// * Factory constructor (don't need new) :
case class Foo(bar: String)
// If not case class, val x = new Foo("s")
val x = Foo("s")
// * All properties are promoted to become immutable fields.
// s = "s"
val s = Foo.bar
// Compiler error: can't assign to val
x.s = "3"
// * Proper definition of hashCode, Equals and toString:
val y = Foo("s")
// True
val same = y == x
// Under the hood, case classes are Products. You can use this property to operate on them in a general way if you so desire.
// Case Classes can be a useful representation of your IR.
// 2. Pattern Matching
val x = "123"
// The compiler properly infers that z must be an int.
val z = x match {
case "456" => 1
case "123" => 2
}
sealed abstract class Vehicle
case class Plane(numSeats: Int, maxSpeed: Int) extends Vehicle
case class Car(numSeats: Int, mpg: Double) extends Vehicle
case class Boat(numHp: Int)
def drive(v: Vehicle) {
// Pattern matching can easily extract attributes from case classes
// Note: Bound attributes are positional, NOT by name.
v match {
case Plane(numSeats, maxSpeed) => { println(numSeats, maxSpeed) }
case Car(numSeats, mpg) => { println(numSeats, mpg)}
case Boat(numHp) => { println(numHp) }
}
}
// Because vehicle is sealed, the compiler knows exactly what subclasses it has.
// Failure to include a subclass in a match is a compiler error:
def nodrive(v: Vehicle) {
// Compiler Error: Match not exhaustive
v match {
case Plane(numSeats, maxSpeed) => { println(numSeats, maxSpeed) }
}
}
// Use _ for wildcard matching
def alldrive(v: Vehicle) {
v match {
case Plane(_, _) => println("It's a plane")
case _ => { println("Something else")}
}
}
// Pattern matching options:
// Match plane objects and bind them to p.
case p: Plane => { ... }
// Extract attributes from case class, bind them to variable names or _ (don't care)
case Plane(numSeats, _) => { ... }
// Extract attributes and capture their container
case p@Plane(_, maxSpeed) => { ... }
// Pattern matching is an extremely powerful pattern that deserves some
// research.
// 3. Partial Functions
// A partial function allows you to encapsulate a match expression in a
// variable.
val is5: PartialFunction[Int, Boolean] = {
case 5 => true
caes _ => false
}
// False
val res = is5(10)
// The partial function library allows a lot of useful operations on partial functions:
// [http://www.scala-lang.org/api/current/index.html#scala.PartialFunction]
// Partial functions have a nice shorthand:
val is5: Int => Boolean = {
case 5 => true
case _ => false
}
// Partial functions are PARTIAL. Calling them with an argument they are not defined for yields a runtime error:
val strictIs5: Int => Boolean = {
case 5 => true
}
// Throws match error
strictIs5(10)
strictIs5(10)
// 4. Type Aliasing
// It can be annoying to type PartialFunction[Int, Boolean] all the time. We
// can alias it to a pithier type:
type Matcher = PartialFunction[Int, Boolean]
// Warning:
// Using the "return" keyword inside of a partial function will return from the calling function! You've been warned.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment