I’ve been doing a lot of Scala programming lately. Scala is a statically typed language that compiles to the JVM. Scala doesn’t break away from object oriented programming but has a lot of functional programming features.

One of my favorite features of Scala is its case classes. Case classes are a lot like regular classes but they’re easier to setup and usually used to model immutable data. They also allow for easy pattern matching.

case class Person(
  name: String,
  age: Int
)

val bob = Person(name="bob", age=20)

Values in a case class are publicly accessible and immutable by default. Note there is no new keyword when instantiating a case class.

Case classes can be compared:

val bob2 = Person(name="bob", age=20)
println(bob == bob2) // true

Since case classes are usually used to model immutable data, Scala gives you a copy function where you can override certain fields. Copy returns a new instance

val youngBob = bob.copy(age=15)

Scala also allows for matching.

bob match {
  case Person("bob", _) => println("hi bob!")
  case _ => println("I don't know you")
}

If you are interested in learning more about Scala, I recommend Scala for the Impatient. If you want to just play around, check out ScalaFiddle.

I like Scala case classes so I thought it would be fun to build them out in Python. Case Classes in Python

See full code below and skip down for discussion and examples:

CaseClass is not actually a class, but a function that returns a class. The function takes in any number of named parameters and their types (Scala is strongly typed after-all). The function raises a ValueError if your parameter value is not a type.

Initializing a CaseClass can be seen below. Remember, the function returns the class.

Person = CaseClass("Person", name=str, age=int)

Now that we have a Person case class, we can create a few Person instances:

bob = Person(name="bob", age=20)

Our case class enforces the right parameters are passed and even the right types:

Person(name="bob") # ValueError: Missing values {'age'}
Person(name="bob", age=20, weight=170) # ValueError: Extra values {'weight'}
Person(name="bob", age=20.5) # ValueError: age is of <class 'float'>, must be of <class 'int'>

And case classes are meant to model immutable data, our values are immutable:

bob.name = "jimmy" # AttributeError: Reassignment to val
bob.zip_code = 90210 # AttributeError: Value zip_code

We also have some useful features, like equality:

bob2 = Person(name="bob", age=20)
young_bob = Person(name="bob", age=15)
bob == bob2 # True
bob == young_bob # False

and copy:

big_bob = bob.copy(name="big bob")

and of course match:

bob.match(
  ({"name": "alice"}, "Its alice!"),
  ({"name": "bob"}, "Hi bob!"),
  (None, "I don't know you")
)

bob.match(
  ({"name": "bob", "age": 15}, "Hi young bob!"),
  ({"name": "bob", "age": 20}, "Hi older bob!"),
  (None, "I don't know you")
)

Sure, it’s not as nice as Scala, but pattern matching is long overdue in Python. Scala has some other features that I didn’t build out like guards on matching (conditional statements), but I’ll leave that to the reader.