Showing posts with label Ad-hoc polymorphism. Show all posts
Showing posts with label Ad-hoc polymorphism. Show all posts

Friday, June 19, 2015

Scala View Bound vs. Context Bound

Target audience: Intermediate
Estimated reading time: 15'


This post illustrates the conversion of class using view bound parameter types into class using context bound types.

Overview

There have been some discussion of deprecating view bound in Scala 2.12. Binding a parameterized type T to a predefined and known type using view (i.e. implicit function conversion) is quite convenient. I have used (or even abuse) this construct in writing machine leaning-based applications.
There are few options for replacing view bound class parameter types with any kind of ad-hoc polymophism techniques. I chose to convert my legacy code by binding the class parameter type to a context instead of a view.

Converting class parameter type binding

First, let's look at binding the parameterized type, T of a class Classifier to the primitive type Double (line 3). The binding is actually implemented by defining an implicit conversion function f: T => Double (line 6) as described in the following code snippet. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def sqr(x: Double): Double = x*x

class Classifier[T <: AnyVal](
   xt: Vector[Array[T]], 
   yt: Vector[Double])
  (implicit f: T => Double) {
 
 def loss(weights: Array[T]): Double = {
   xt.zip(yt).map{ case(x, y) => 
     y - x.zip(weights).map{ case(_x, w)
       => _x*w}.sum }.map(sqr(_)).sum
}

In this example, the observed features and the model parameters weights have the same type: array of element T (line 8). The input time series xt is a vector of observations and the labels (or expected outcome) yt is a vector of single variable observation of type Double (line 9). The implicit conversion apply to the features and weights. Scala provides developers with shorter alternative notation T <% Uas follows: 

class Classifier[T <% Double](
   xt: Vector[Array[T]], 
   yt: Vector[Double]) { }

Replacing the binding of the class parameters for many classes would be tedious and error prone. One solution is to create a template or pattern that can be applied to several section of the code base.
The process of binding the type T of a class parameter to another known parameter type consists of defining a value (or context) Feature for the type T (line 1). The context binding is an implicit value conversion T => Feature[T] (line 3). 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
trait Feature[T]{ def apply(x: T): Array[Double] }
 
class Classifier[T : Feature](
   xt: Vector[T],
   yt: Vector[Double]) {
  
 def loss(weights: T): Double = {
   xt.zip(yt).map{case(x, y) => {
     val mapper: Feature[T] = implicitly[Feature[T]]
     y - mapper(x).zip(mapper(weights)).map{ case(x, w) =>x*w}.sum
    }}.map(sqr(_)).sum
  }
}

The Feature trait defines the context for the conversion which is actually implemented by the apply method (line 1). The view binding implicit function T => Double defined in the first implementation of the Classifier class is replaced by the mapping (or conversion) method: Feature.apply: T => Array[Double]
The mapping function mapper is implicitly declared using the implicitly.

The notation T: Feature specifies the implicit value, feature: It is equivalent to the more descriptive declaration of the Classifier class: 

1
2
3
class Classifier[T](
   xt: Vector[T],
   yt: Vector[Double])(implicit feature: Feature[T])

Important note
A class parameterized type T cannot be bound to a primitive type P using a context: the developer has to define either an implicit view (T => P) or a context (or wrapper) W for the type T => W[P].

References

Friday, October 5, 2012

Type Class Polymorphism and Monads

Target audience: Advanced

Overview

Most of software developers associate the concept of polymorphism to dynamic binding & class inheritance available in the most common object oriented programming languages such as C++ and Java. As part of its functional programming "DNA", Scala provides developers with  alternative polymorphism approaches (beside the ubiquitous method override).

Note: For the sake of readability of the implementation of algorithms, all non-essential code such as error checking, comments, exception, validation of class and method arguments, scoping qualifiers or import is omitted.


Object-Oriented Polymorphism

Java or C++ programmers are familiar with this implementation of polymorphism as described in the example below. A trait the key behavior of a collection by declaring two method += to add a new item into the collection and -= to retrieve the first item from the collection.

trait Collection[T <: Double] {                           
   def += (t:T) : Boolean
   def -= : T 
}

The class, OrderedCollection implements ordered collection of type inherited from Double by defining the two abstract methods += & -=. This class specializes the behavior of the trait by ordering the elements in the collection.

class OrderedCollection[T <: Double] extends Collection[T] {
   import scala.collection.mutable.TreeSet
 
   var c = TreeSet.empty(Ordering.fromLessThan[T]((x:Double, y:Double) => (x < y)))
   override def += (t:T) : Boolean = c.add(t)
   override def -= : T = { 
     val top = c.head
     c.remove(top)
     top 
   }
}

The following code snippet illustrate a simple use case for the ordered collection. Note the declaration of the type of the collection instance, thisCollection is not required and added for the sake of clarity.

val thisCollection: Collection[Double] = new OrderedCollection[Double]
thisCollection += 9.556
thisCollection += 45.7
thisCollection += 4.9

println(thisCollection)


Type Class Polymorphism 

Scala supports Type class polymorphism, also referred as Ah-hoc polymorphism in technical articles. Scala also supports Ah-Doc polymorphism which the ability to create and apply generic polymorphic operators or functions to arguments of different types. Ad hoc polymorphism lets you add features to a type on demand.
Let's define a Wrapper type (trait) which basic purpose is to wrap an element into a collection M[_] of this element. The Wrapper type class declares the operators apply and get that is to be implemented by wrapper associated to a predetermined collection.
- apply creates a collection M[T] of element of type T.
- get retrieves the first and only element of the collection.

Note: The type of the element in the collection is not a parameter type for the Wrapper: T is the type parameter for the collection and type M[_] is the parameter type for the wrapper. The Wrapper trait is defined as:

trait Container[M[_]] {
  def apply[T](t: T): M[T]
  def get[T](c: M[T]): T
}

We can create a type class ArrayBuffer to "wrap" any object by overriding the apply and get operators. The implicit conversion from ArrayBuffer to Wrapper is obviously independent from the type T of the contained element (validation of method argument is omitted).

implicit val arrayBuffer = new Container[ArrayBuffer] {
  override def apply[T](t: T) : ArrayBuffer[T] =  ArrayBuffer[T](t)
  override def get[T](ab: ArrayBuffer[T]) : T = ab.head 
}

def compose[M[_]: Container, X, Y](c1: M[X], c2: M[Y]) = {
   val c = implicitly[Container[M]]
   c(ab.get(c1), ab.get(c2))
}


Monads

One of the most known application of type class polymorphism is the monadic representation of collection. The concept and theoretical foundation of monads is discussed in the previous post. Monads define the minimum set of operations for a functional category of objects. Scala standard library includes quite a few of categories and their associated monadic representation: List[T], Option[T], ..
Let's extend our simple Wrapper to define a Monad for collection containing a single element. The monad must define a constructor, apply, a functor, map that converts a single element collection into a another single element collection by transforming its only element and a flatMap that generate a new single element collection from the element of an existing collection.

trait _Monad[M[_]] {
  def apply[T](t: T): M[T]
  def map[T, U](m: M[_])(f: T => U): M[U]
  def flatMap[T, U](m: M[_])(f: T => M[U]): M[U]
}

As with the trait wrapper we can create a implicit conversion from any single element ArrayBuffer into its associated monad to monadic operators can be used transparently by the client code.

implicit val mArrayBuffer = new _Monad[ArrayBuffer] {
  override def apply[T](t:T): ArrayBuffer[T] =  ArrayBuffer[T](t)
  override def map[T, U](m: ArrayBuffer[T])(f: T => U): ArrayBuffer[U] = ArrayBuffer[U](f(m.head))
  override def flatMap[T, U](m: ArrayBuffer[T])(f: T => ArrayBuffer[U]): ArrayBuffer[U] = f(m.head) 
}

Monads will be discussed in length in a future post.


References