This lesson covers:

View bounds ("type classes")

Sometimes you don't need to specify that one type is equal/sub/super another, just that you could fake it with conversions. A view bound specifies a type that can be "viewed as" another. This makes sense for an operation that needs to "read" an object but doesn't modify the object.

Implicit functions allow automatic conversion. More precisely, they allow on-demand function application when this can help satisfy type inference. e.g.:

In []:
implicit def strToInt(x: String) = x.toInt
strToInt: (x: String)Int
In []:
"123"
Out[]:
123
In []:
val y: Int = "123"
Out[]:
123
In []:
math.max("123", 111)
Out[]:
123

view bounds, like type bounds demand such a function exists for the given type. You specify a type bound with <% e.g.,

In []:
class Container[A <% Int] { def addIt(x: A) = 123 + x }
defined class Container

This says that A has to be "viewable" as Int. Let's try it.

In []:
(new Container[String]).addIt("123")
Out[]:
246
In []:
(new Container[Int]).addIt(123)
Out[]:
246
In []:
(new Container[Float]).addIt(123.2F)
<console>:8: error: could not find implicit value for evidence parameter of type (Float) => Int
       (new Container[Float]).addIt(123.2)
        ^

Other type bounds

Methods can enforce more complex type bounds via implicit parameters. For example, List supports sum on numeric contents but not on others. Alas, Scala's numeric types don't all share a superclass, so we can't just say T <: Number. Instead, to make this work, Scala's math library defines an implicit Numeric for the appropriate types T. Then in List's definition uses it:

sum[B >: A](implicit num: Numeric[B]): B

If you invoke List(1,2).sum(), you don't need to pass a num parameter; it's set implicitly. But if you invoke List("whoop").sum(), it complains that it couldn't set num.

Methods may ask for some kinds of specific "evidence" for a type without setting up strange objects as with Numeric. Instead, you can use of these type-relation operators:

|A =:= B|A must be equal to B|
|A <:< B|A must be a subtype of B|
|A <%< B|A must be viewable as B|
In []:
class Container[A](value: A) { def addIt(implicit evidence: A =:= Int) = 123 + value }
defined class Container
In []:
(new Container(123)).addIt
Out[]:
246
In []:
(new Container("123")).addIt
Out[]:
<console>:10: error: could not find implicit value for parameter evidence: =:=[java.lang.String,Int]

Similarly, given our previous implicit, we can relax the constraint to viewability:

In []:
class Container[A](value: A) { def addIt(implicit evidence: A <%< Int) = 123 + value }
In []:
(new Container("123")).addIt
Out[]:
246

Generic programming with views

In the Scala standard library, views are primarily used to implement generic functions over collections. For example, the min function (on Seq[]), uses this technique:

def min[B >: A](implicit cmp: Ordering[B]): A = {
  if (isEmpty)
    throw new UnsupportedOperationException("empty.min")

  reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y)
}

The main advantages of this are:

In []:
List(1,2,3,4).min
Out[]:
1
In []:
List(1,2,3,4).min(new Ordering[Int] { def compare(a: Int, b: Int) = b compare a })
Out[]:
4

As a sidenote, there are views in the standard library that translates Ordered into Ordering (and vice versa).

In []:
trait LowPriorityOrderingImplicits {
    implicit def ordered[A <: Ordered[A]]: Ordering[A] = new Ordering[A] {
        def compare(x: A, y: A) = x.compare(y)
    }
}

Context bounds & implicitly[]

Scala 2.8 introduced a shorthand for threading through & accessing implicit arguments.

In []:
def foo[A](implicit x: Ordered[A]) {}
In []:
def foo[A : Ordered] {}

Implicit values may be accessed via implicitly

In []:
implicitly[Ordering[Int]]
Out[]:
scala.math.Ordering$Int$@3a9291cf

Combined, these often result in less code, especially when threading through views.

Higher-kinded types & ad-hoc polymorphism

Scala can abstract over "higher kinded" types. For example, suppose that you needed to use several types of containers for several types of data. You might define a Container interface that might be implemented by means of several container types: an Option, a List, etc. You want to define an interface for using values in these containers without nailing down the values' type.

This is analogous to function currying. For example, whereas "unary types" have constructors like List[A], meaning we have to satisfy one "level" of type variables in order to produce a concrete types (just like an uncurried function needs to be supplied by only one argument list to be invoked), a higher-kinded type needs more.

In []:
trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A }
In []:
val container = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head }
Out[]:
$anon$1@7c8e3f75
In []:
container.put("hey")
Out[]:
List(hey)
In []:
container.put(123)
Out[]:
List(123)

Note that Container is polymorphic in a parameterized type ("container type").

If we combine using containers with implicits, we get "ad-hoc" polymorphism: the ability to write generic functions over containers.

In []:
trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A }
In []:
implicit val listContainer = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head }
In []:
implicit val optionContainer = new Container[Some] { def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get }
In []:
def tupleize[M[_]: Container, A, B](fst: M[A], snd: M[B]) = {
    val c = implicitly[Container[M]]
    c.put(c.get(fst), c.get(snd))
}
In []:
tupleize(Some(1), Some(2))
Out[]:
Some((1,2))
In []:
tupleize(List(1), List(2))
Out[]:
List((1,2))

F-bounded polymorphism

Often it's necessary to access a concrete subclass in a (generic) trait. For example, imagine you had some trait that is generic, but can be compared to a particular subclass of that trait.

trait Container extends Ordered[Container]

However, this now necessitates the compare method

def compare(that: Container): Int

And so we cannot access the concrete subtype, e.g.:

class MyContainer extends Container {
    def compare(that: MyContainer): Int
}

fails to compile, since we are specifying Ordered for Container, not the particular subtype.

To reconcile this, we instead use F-bounded polymorphism.

trait Container[A <: Container[A]] extends Ordered[A]

Strange type! But note now how Ordered is parameterized on A, which itself is Container[A]

So, now

class MyContainer extends Container[MyContainer] {
  def compare(that: MyContainer) = 0
}

They are now ordered:

In []:
List(new MyContainer, new MyContainer, new MyContainer)
Out[]:
List(MyContainer@30f02a6d, MyContainer@67717334, MyContainer@49428ffa)
In []:
List(new MyContainer, new MyContainer, new MyContainer).min
Out[]:
MyContainer@33dfeb30

Given that they are all subtypes of Container[_], we can define another subclass & create a mixed list of Container[_]:

In []:
class YourContainer extends Container[YourContainer] { def compare(that: YourContainer) = 0 }
In []:
List(new MyContainer, new MyContainer, new MyContainer, new YourContainer)
Out[]:
List(MyContainer@3be5d207, MyContainer@6d3fe849, MyContainer@7eab48a7, YourContainer@1f2f0ce9)

Note how the resulting type is now lower-bound by YourContainer with MyContainer. This is the work of the type inferencer. Interestingly- this type doesn't even need to make sense, it only provides a logical greatest lower bound for the unified type of the list. What happens if we try to use Ordered now?

In []:
(new MyContainer, new MyContainer, new MyContainer, new YourContainer).min
Out[]:
<console>:9: error: could not find implicit value for parameter cmp:
  Ordering[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]]

No Ordered[] exists for the unified type. Too bad.

Structural types

Scala has support for structural types -- type requirements are expressed by interface structure instead of a concrete type.

In []:
def foo(x: { def get: Int }) = 123 + x.get
In []:
foo(new { def get = 10 })

This can be quite nice in many situations, but the implementation uses reflection, so be performance-aware!

Abstract type members

In a trait, you can leave type members abstract.

In []:
trait Foo { type A; val x: A; def getX: A = x }
In []:
(new Foo { type A = Int; val x = 123 }).getX
Out[]:
123
In []:
(new Foo { type A = String; val x = "hey" }).getX
hey

This is often a useful trick when doing dependency injection, etc.

You can refer to an abstract type variable using the hash-operator:

In []:
trait Foo[M[_]] { type t[A] = M[A] }
In []:
val x: Foo[List]#t[Int] = List(1)
Out[]:
List(1)

Type erasures & manifests

As we know, type information is lost at compile time due to erasure. Scala features Manifests, allowing us to selectively recover type information. Manifests are provided as an implicit value, generated by the compiler as needed.

In []:
class MakeFoo[A](implicit manifest: Manifest[A]) { def make: A = manifest.erasure.newInstance.asInstanceOf[A] }
In []:
(new MakeFoo[String]).make
Out[]:
""

Case study: Finagle

See: https://github.com/twitter/finagle

In []:
trait Service[-Req, +Rep] extends (Req => Future[Rep])

trait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn]
  extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut])
{
  def andThen[Req2, Rep2](next: Filter[ReqOut, RepIn, Req2, Rep2]) =
    new Filter[ReqIn, RepOut, Req2, Rep2] {
      def apply(request: ReqIn, service: Service[Req2, Rep2]) = {
        Filter.this.apply(request, new Service[ReqOut, RepIn] {
          def apply(request: ReqOut): Future[RepIn] = next(request, service)
          override def release() = service.release()
          override def isAvailable = service.isAvailable
        })
      }
    }

  def andThen(service: Service[ReqOut, RepIn]) = new Service[ReqIn, RepOut] {
    private[this] val refcounted = new RefcountedService(service)

    def apply(request: ReqIn) = Filter.this.apply(request, refcounted)
    override def release() = refcounted.release()
    override def isAvailable = refcounted.isAvailable
  }
}

A service may authenticate requests with a filter.

In []:
trait RequestWithCredentials extends Request {
  def credentials: Credentials
}

class CredentialsFilter(credentialsParser: CredentialsParser)
  extends Filter[Request, Response, RequestWithCredentials, Response]
{
  def apply(request: Request, service: Service[RequestWithCredentials, Response]): Future[Response] = {
    val requestWithCredentials = new RequestWrapper with RequestWithCredentials {
      val underlying = request
      val credentials = credentialsParser(request) getOrElse NullCredentials
    }

    service(requestWithCredentials)
  }
}

Note how the underlying service requires an authenticated request, and that this is statically verified. Filters can thus be thought of as service transformers.

Many filters can be composed together:

In []:
val upFilter =
  logTransaction     andThen
  handleExceptions   andThen
  extractCredentials andThen
  homeUser           andThen
  authenticate       andThen
  route

Type safely!