Type Parameterization

download Type Parameterization

If you can't read please download the document

Transcript of Type Parameterization

Type Parameterization

Satendra Kumar Software ConsultantKnoldus Software LLP

Class hierarchy

What is type

Any class, trait or object is a type

Anything define by type keyword is a type.

For example type A = String

Type Parameters

class C[T]

Type Param

Type Constructor

Why we use type parameterization ?

abstract class IntStack { def push(x: Int): IntStack = new IntNonEmptyStack(x, this) def isEmpty: Boolean def top: Int def pop: IntStack}

class IntEmptyStack extends IntStack { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop")}

class IntNonEmptyStack(elem: Int, rest: IntStack) extends IntStack { def isEmpty = false def top = elem def pop = rest}

abstract class IntStack { def push(x: Int): IntStack = new IntNonEmptyStack(x, this) def isEmpty: Boolean def top: Int def pop: IntStack}

class IntEmptyStack extends IntStack { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop")}

class IntNonEmptyStack(elem: Int, rest: IntStack) extends IntStack { def isEmpty = false def top = elem def pop = rest}

abstract class StringStack { def push(x: String): StringStack = new StringNonEmptyStack(x, this) def isEmpty: Boolean def top: String def pop: StringStack}

class StringEmptyStack extends StringStack { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop")}

class StringNonEmptyStack(elem: String, rest: StringStack) extends StringStack { def isEmpty = false def top = elem def pop = rest}

For Int

For String

abstract class Stack[A] { def push(x: A): Stack[A] = new NonEmptyStack[A](x, this) def isEmpty: Boolean def top: A def pop: Stack[A]}class EmptyStack[A] extends Stack[A] { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop")}class NonEmptyStack[A](elem: A, rest: Stack[A]) extends Stack[A] { def isEmpty = false def top = elem def pop = rest}

Generic version of Stack

As an example, here is a generic method which determines whether one stack is a prefix of another.

def isPrefix[A](p: Stack[A], s: Stack[A]): Boolean = {

p.isEmpty || p.top == s.top && isPrefix[A](p.pop, s.pop)

}

Generic method

As an example, here is a generic method which determines whether one stack is a prefix of another.

def isPrefix[A](p: Stack[A], s: Stack[A]): Boolean = {

p.isEmpty || p.top == s.top && isPrefix[A](p.pop, s.pop)

}

Generic method

The method parameters are called polymorphic. Generic methods are also called polymorphic.

Why we use type parameterization ?

Type parameterization allows you to write generic classes, methods and traits.

Type Variance

A type parameter of a class or trait can be marked with a variance annotation, either covariant ( + ) or contravariant ( - ). Such variance annotations indicate how subtyping works for a generic class or trait.

class C[T] //in-variant

class C[+T] //co-variant

class C[-T] //contra-variant

class Parent

class Child extends Parent

in-variant

A type parameter of a class or trait is by default nonvariant.The class or trait then does not subtype when that parameter changes.

For example, class Array is non-variant in its type parameter,Array[String] is neither a subtype nor a supertype of Array[AnyRef].

final class Array[T] extends java.io.Serializable with java.lang.Cloneable

in-variant

class C[T] //in-variant or non-variant

val x:C[Parent] = new C[Parent]

val x: C[Parent] = new C[Child]error: type mismatch; found : C[Child] required: C[Parent]Note: Child : Child, but class C is invariant in type T. You may wish to define T as -T instead. (SLS 4.5) val x: C[Child] = new C[Parent]

in-variant in Scala Standard Library

final class Array[T] extends java.io.Serializable with java.lang.Cloneable

trait Set[A] extends (A) Boolean with Iterable[A] with GenSet[A] with GenericSetTemplate[A, Set] with SetLike[A, Set[A]] // mutable

trait Set[A] extends Iterable[A] with collection.Set[A] with GenericSetTemplate[A, Set] with SetLike[A, Set[A]] with Parallelizable[A, ParSet[A]] //immutable

final class ListBuffer[A] extends AbstractBuffer[A] with Buffer[A] with GenericTraversableTemplate[A, ListBuffer] with BufferLike[A, ListBuffer[A]] with Builder[A, immutable.List[A]] with SeqForwarder[A] with java.io.Serializable //mutable

class ListMap[A, B] extends AbstractMap[A, B] with Map[A, B] with MapLike[A, B, ListMap[A, B]] with Serializable // mutable

Mutable collection in Scala is in-variant.

Why is Scala's immutable Set invariant in its type?

Why is Scala's immutable Set invariant in its type?

http://www.scala-lang.org/old/node/9764

On the issue of sets, I believe the non-variance stems also from the implementations.Common sets are implemented as hashtables, which are non-variant arrays of the key type. I agree it's a slighly annoying irregularity.

-- Martin

co-variant

A covariant annotation can be applied to a type parameter of a class or trait by putting a plus sign ( + ) before the type parameter. The class or trait then subtypes covariantly with in the same direction as the type annotated parameter.

For example, List is covariant in its type parameter, so List[String] is a subtype of List[AnyRef] .

sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A]

co-variant

class C[+T] //co-variant

val x:C[Parent] = new C[Parent]

val x: C[Parent] = new C[Child]

val x: C[Child] = new C[Parent] error: type mismatch; found : C[Parent] required: C[Child] Note: Parent >: Child, but class C is invariant in type T. You may wish to define T as -T instead. (SLS 4.5) val x: C[Child] = new C[Parent]

co-variant in Scala Standard Library

sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqOptimized[A, List[A]] with java.io.Serializable

final class Vector[+A] extends AbstractSeq[A] with IndexedSeq[A] with GenericTraversableTemplate[A, Vector] with IndexedSeqLike[A, Vector[A]] with VectorPointer[A] with Serializable with CustomParallelizable[A, ParVector[A]]

trait Iterable[+A] extends Traversable[A] with collection.Iterable[A] with GenericTraversableTemplate[A, Iterable] with IterableLike[A, Iterable[A]] with Parallelizable[A, ParIterable[A]]

trait Seq[+A] extends PartialFunction[Int, A] with Iterable[A] with GenSeq[A] with GenericTraversableTemplate[A, Seq] with SeqLike[A, Seq[A]]

Immutable collection in Scala is co-variant.

In Java Array is co-variant but in Scala Array is in-variant ?

In Java Array is co-variant but in Scala Array is in-variant ?

class Test {

public static void main(String[] arg) {

String[] a1 = { "abc" };Object[] a2 = a1;a2[0] = new Integer(17);String s = a1[0];}

}

In Java Array is co-variant but in Scala Array is in-variant ?

// In Java class Test {

public static void main(String[] arg) {

String[] a1 = { "abc" };Object[] a2 = a1;a2[0] = new Integer(17);String s = a1[0];}

}

If you try out this example, you will find that it compiles, but executing theprogram will cause an ArrayStore exception to be thrown when a2[0] isassigned to an Integer

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integerat Test.main(Test.java:7)

In Java Array is co-variant but in Scala Array is in-variant ?

val a1: Array[String] = Array("a", "b", "c") val a2: Array[AnyRef] = a1 a2(0) = 17 val s:String = a1(0)

In Java Array is co-variant but in Scala Array is in-variant ?

// In Scala val a1: Array[String] = Array("a", "b", "c") val a2: Array[AnyRef] = a1 a2(0) = 17 val s = a1(0)

type mismatch; found : Array[String] required: Array[AnyRef] Note: String : A , defines A as the lower bound for B . As a result, B is required to be a supertype of A. The parameter to push is now of type B instead of type A , and the return value of the method is now Stack[B] instead of Stack[A] .

abstract class Stack[+A] { def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x, this) def isEmpty: Boolean def top: A def pop: Stack[A]}class EmptyStack[+A] extends Stack[A] { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop")}class NonEmptyStack[+A](elem: A, rest: Stack[A]) extends Stack[A] { def isEmpty = false def top = elem def pop = rest}

val x: EmptyStack[Int] = new EmptyStack[Int] val y: Stack[Int] = x.push(1).push(2) val z:Stack[Any] =y val r:Stack[Any] =z.push("hello")

Upper bounds

def orderedMergeSort[T ys case (_, Nil) => xs case (x :: xs1, y :: ys1) => if (x < y) x :: merge(xs1, ys) else y :: merge(xs, ys1) } val n = xs.length / 2 if (n == 0) xs else { val (ys, zs) = xs splitAt n merge(orderedMergeSort(ys), orderedMergeSort(zs)) } }

class Person(val firstName: String, val lastName: String) extends Ordered[Person] {

def compare(that: Person) = { val lastNameComparison = lastName.compareToIgnoreCase(that.lastName) if (lastNameComparison != 0) lastNameComparison else firstName.compareToIgnoreCase(that.firstName) }

override def toString = firstName + " " + lastName

}

val people = List( new Person("Larry", "Wall"), new Person("Anders", "Hejlsberg"), new Person("Guido", "van Rossum"), new Person("Alan", "Kay"), new Person("Yukihiro", "Matsumoto")) val sortedPeople = orderedMergeSort(people)

Type Inference

Type inference refers to the automatic deduction of the data type of an expression in a programming language.

Scala has a built-in type inference mechanism which allows the programmer to omit certain type annotations. It is, for instance, often not necessary in Scala to specify the type of a variable, since the compiler can deduce the type from the initialization expression of the variable. Also return types of methods can often be omitted since they corresponds to the type of the body, which gets inferred by the compiler.

Type Inference

scala> val x :Int = 2x: Int = 2

scala> val y: String = "hello"y: String = hello

scala> val y: Map[Int,String] = Map[Int,String](1 -> "one")y: Map[Int,String] = Map(1 -> one)

Type Inference

scala> val x :Int = 2x: Int = 2

scala> val y: String = "hello"y: String = hello

scala> val y: Map[Int,String] = Map[Int,String](1 -> "one")y: Map[Int,String] = Map(1 -> one)

scala> val x = 2x: Int = 2

scala> val y = "hello"y: String = hello

scala> val y = Map(1 -> "one")y: scala.collection.immutable.Map[Int,String] = Map(1 -> one)

Type Inference

List(1, 2, 3, 4).filter { a: Int => a > 1 }

Type Inference

List(1, 2, 3, 4).filter { a: Int => a > 1 }

List(1, 2, 3, 4).filter { a => a > 1 }

Type Inference

List(1, 2, 3, 4).filter { a: Int => a > 1 }

List(1, 2, 3, 4).filter { a => a > 1 }

List(1, 2, 3, 4).filter { _ > 1 }

Limitation Of Type Inference

For recursive methods, the compiler is not able to infer a result type. Here is a program which will fail the compiler for this reason:

def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1)

Thanks