If you're new to scala, here are some specific patterns.
The cake pattern is the quintesential scala pattern made possible by traits as opposed to interfaces: link.
Pimping is adding methods to another class, by essentially definiting an implicit cast to a new type which defines that method. The compiler will automatically use the implicit cast when the method is used.
Benefits are many, including no dependency on the base class, no poluting of the name space (at least no visible or known to the user) etc. This is related to:
Read the original article
Quite possibly the best example for this in the scala libraries is adding the functional methods to the Java collections:
import scala.collection.JavaConversions._
val resp = new java.util.ArrayList()
...
resp.filter ( _.contains("ahoi") ).size
Type classes are a concept explored originally in Haskell and provides a different kind of polymorphism than usual strait-up OO class inheritance.
To attempt to briefly introduce the pattern: instead of inheriting a trait or interface, the respective functionality is defined as a type class and implementations are provided for the different subsequent types. The compiler then figures out which implementation to use in different contexts, transparent to the client code.
Scala permits the same pattern, via a mix of traits, modules (for scoping), together with implicits and associated context bounds etc. These are some of the good articles on the subject:
You should read the articles above for a detailed understanding. Here's a simple comparison I derived, for clarity:
//============== the OO version
trait Eq[A] { // the type trait
def sameAs(b:A) : Boolean // so many name clashes with eq/equals etc
}
case class Student (name:String) extends Eq[Student] { // classic OO implementation
override def sameAs(b:Student) = this.name == b.name
}
// someone actually using the Eq: A must be a subtype of Eq
def member[A <: Eq[A]] (a:A, container:List[A]) =
container.exists(_.sameAs(a))
member (Student("John"), List(Student("John")))
and
//============== the Type Class version
trait Eq[A] { // the type trait
def areTheSame(a: A, b: A): Boolean
}
case class Student(name: String) // better decoupling - extends nothing
object EqImplementations { // the implementations for different classes
implicit object EqStudent extends Eq[Student] {
override def areTheSame(a: Student, b: Student) = a.name == b.name
}
}
// all it says is that there must be an Eq implementation for A, somewhere in context
def member[A: Eq](a: A, container: List[A]) =
container.exists(implicitly[Eq[A]].areTheSame(_, a))
import EqImplementations._ // bring it in context
member(Student("John"), List(Student("John")))
In the second version, the implementation for Eq corresponding to Student is figured out implicitly. The type class version is a little longer but the benefits include decoupling the Student from Eq.