Type ParameterizationType Parameterization
Satendra Kumar
Software Consultant
Knoldus Software LLP
Satendra Kumar
Software Consultant
Knoldus 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 <: Parent, but class C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
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]
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 the
program will cause an ArrayStore exception to be thrown when a2[0] is
assigned to an Integer
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
at 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
<: AnyRef, but class Array is invariant in type T. You may wish
to investigate a wildcard type such as _ <: AnyRef. (SLS 3.2.10)
In Scala, this example does not compile. You will get compile time error :
why Java adopted this design, which seems both unsafe and expensive ?
Why Java adopted this design, which seems both unsafe and expensive ?
James Gosling answered that they wanted to have a simple means to treat arrays
generically. For instance, they wanted to be able to write a method to sort all
elements of an array, using a signature like the following that takes an array of
Object :
Covariance of arrays was needed so that arrays of arbitrary reference types could
be passed to this sort method. Of course, with the arrival of Java generics, such a
sort method can now be written with a type parameter, so the covariance of arrays
is no longer necessary. For compatibility reasons,though, it has persisted in Java to
this day.
void sort(Object[] a, Comparator cmp) { ... }
A contravariant annotation can be applied to a type parameter of a class or trait by putting
a minus sign ( - ) before the type parameter. The class or trait then subtypes contravariantly
with in the opposite direction as the type annotated parameter.
For example, Function1 is contravariant in its first type parameter, and so Function1[Any,
Any] is a subtype of Function1[String, Any] .
contra-variant
trait Function1[-T1, +R] extends AnyRef
contra-variant
class C[-T] //contra-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 <: Parent, but class C is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
val x: C[Parent] = new C[Child]
val x: C[Child] = new C[Parent]
contra-variant in Scala Standard Library
trait Function1[-T1, +R] extends AnyRef
trait Function2[-T1, -T2, +R] extends AnyRef
trait PartialFunction[-A, +B] extends (A) ⇒ B
class Publication(val title: String)
class Book(title: String) extends Publication(title)
object Library {
val books: Set[Book] =
Set(
new Book("Programming in Scala"),
new Book("Walden"))
def printBookList(info: Book => AnyRef) {
for (book <- books) println(info(book))
}
}
class Publication(val title: String)
class Book(title: String) extends Publication(title)
object Library {
val books: Set[Book] =
Set(
new Book("Programming in Scala"),
new Book("Walden"))
def printBookList(info: Book => AnyRef) {
for (book <- books) println(info(book))
}
}
object Customer extends App {
def getTitle(p: Publication): String = p.title
Library.printBookList(getTitle)
}
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
}
val x: EmptyStack[Int] = new EmptyStack[Int]
val y: Stack[Int] = x.push(1).push(2)
println(y.pop.top)
We can do:
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
}
val x: EmptyStack[Int] = new EmptyStack[Int]
val y: Stack[Int] = x.push(1).push(2)
val z:Stack[Any] =y
val r:Stack[Int] =z.push("hello")
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
}
val x: EmptyStack[Int] = new EmptyStack[Int]
val y: Stack[Int] = x.push(1).push(2)
val z:Stack[Any] =y
val r:Stack[Int] =z.push("hello")
Would it compile ?
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
}
val x: EmptyStack[Int] = new EmptyStack[Int]
val y: Stack[Int] = x.push(1).push(2)
val z:Stack[Any] =y
val r:Stack[Int] =z.push("hello")
Answer => No
Lower Bounds
def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x, this)
The new definition gives push a type parameter B , and with the syntax, “ B >: 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 <: Ordered[T]](xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => 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 = 2
x: 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 = 2
x: 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 = 2
x: 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)
ThanksThanks

Type Parameterization

  • 1.
    Type ParameterizationType Parameterization SatendraKumar Software Consultant Knoldus Software LLP Satendra Kumar Software Consultant Knoldus Software LLP
  • 2.
  • 3.
    What is type ●Any class, trait or object is a type ● Anything define by type keyword is a type. For example type A = String
  • 4.
    Type Parameters class C[T] TypeParam Type Constructor
  • 5.
    Why we usetype parameterization ?
  • 6.
    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 }
  • 7.
    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
  • 8.
    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
  • 9.
    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
  • 10.
    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.
  • 11.
    Why we usetype parameterization ? Type parameterization allows you to write generic classes, methods and traits.
  • 12.
    Type Variance A typeparameter 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
  • 13.
  • 14.
    in-variant A type parameterof 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
  • 15.
    in-variant class C[T] //in-variantor 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 <: Parent, but class C is invariant in type T. You may wish to define T as +T instead. (SLS 4.5) 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]
  • 16.
    in-variant in ScalaStandard 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.
  • 17.
    Why is Scala'simmutable Set invariant in its type?
  • 18.
    Why is Scala'simmutable 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
  • 19.
    co-variant A covariant annotationcan 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]
  • 20.
    co-variant class C[+T] //co-variant valx: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]
  • 21.
    co-variant in ScalaStandard 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.
  • 22.
    In Java Arrayis co-variant but in Scala Array is in-variant ?
  • 23.
    In Java Arrayis 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]; } }
  • 24.
    In Java Arrayis 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 the program will cause an ArrayStore exception to be thrown when a2[0] is assigned to an Integer Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer at Test.main(Test.java:7)
  • 25.
    In Java Arrayis 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)
  • 26.
    In Java Arrayis 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 <: AnyRef, but class Array is invariant in type T. You may wish to investigate a wildcard type such as _ <: AnyRef. (SLS 3.2.10) In Scala, this example does not compile. You will get compile time error :
  • 27.
    why Java adoptedthis design, which seems both unsafe and expensive ?
  • 28.
    Why Java adoptedthis design, which seems both unsafe and expensive ? James Gosling answered that they wanted to have a simple means to treat arrays generically. For instance, they wanted to be able to write a method to sort all elements of an array, using a signature like the following that takes an array of Object : Covariance of arrays was needed so that arrays of arbitrary reference types could be passed to this sort method. Of course, with the arrival of Java generics, such a sort method can now be written with a type parameter, so the covariance of arrays is no longer necessary. For compatibility reasons,though, it has persisted in Java to this day. void sort(Object[] a, Comparator cmp) { ... }
  • 29.
    A contravariant annotationcan be applied to a type parameter of a class or trait by putting a minus sign ( - ) before the type parameter. The class or trait then subtypes contravariantly with in the opposite direction as the type annotated parameter. For example, Function1 is contravariant in its first type parameter, and so Function1[Any, Any] is a subtype of Function1[String, Any] . contra-variant trait Function1[-T1, +R] extends AnyRef
  • 30.
    contra-variant class C[-T] //contra-variant valx:C[Parent] = new C[Parent] val x: C[Parent] = new C[Child] error: type mismatch; found : C[Child] required: C[Parent] Note: Child <: Parent, but class C is invariant in type T. You may wish to define T as +T instead. (SLS 4.5) val x: C[Parent] = new C[Child] val x: C[Child] = new C[Parent]
  • 31.
    contra-variant in ScalaStandard Library trait Function1[-T1, +R] extends AnyRef trait Function2[-T1, -T2, +R] extends AnyRef trait PartialFunction[-A, +B] extends (A) ⇒ B
  • 32.
    class Publication(val title:String) class Book(title: String) extends Publication(title) object Library { val books: Set[Book] = Set( new Book("Programming in Scala"), new Book("Walden")) def printBookList(info: Book => AnyRef) { for (book <- books) println(info(book)) } }
  • 33.
    class Publication(val title:String) class Book(title: String) extends Publication(title) object Library { val books: Set[Book] = Set( new Book("Programming in Scala"), new Book("Walden")) def printBookList(info: Book => AnyRef) { for (book <- books) println(info(book)) } } object Customer extends App { def getTitle(p: Publication): String = p.title Library.printBookList(getTitle) }
  • 35.
    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 } val x: EmptyStack[Int] = new EmptyStack[Int] val y: Stack[Int] = x.push(1).push(2) println(y.pop.top) We can do:
  • 36.
    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 } val x: EmptyStack[Int] = new EmptyStack[Int] val y: Stack[Int] = x.push(1).push(2) val z:Stack[Any] =y val r:Stack[Int] =z.push("hello")
  • 37.
    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 } val x: EmptyStack[Int] = new EmptyStack[Int] val y: Stack[Int] = x.push(1).push(2) val z:Stack[Any] =y val r:Stack[Int] =z.push("hello") Would it compile ?
  • 38.
    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 } val x: EmptyStack[Int] = new EmptyStack[Int] val y: Stack[Int] = x.push(1).push(2) val z:Stack[Any] =y val r:Stack[Int] =z.push("hello") Answer => No
  • 39.
    Lower Bounds def push[B>: A](x: B): Stack[B] = new NonEmptyStack(x, this) The new definition gives push a type parameter B , and with the syntax, “ B >: 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] .
  • 40.
    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")
  • 41.
    Upper bounds def orderedMergeSort[T<: Ordered[T]](xs: List[T]): List[T] = { def merge(xs: List[T], ys: List[T]): List[T] = (xs, ys) match { case (Nil, _) => 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)) } }
  • 42.
    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)
  • 43.
    Type Inference Type inferencerefers 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.
  • 44.
    Type Inference scala> valx :Int = 2 x: 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)
  • 45.
    Type Inference scala> valx :Int = 2 x: 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 = 2 x: 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)
  • 46.
    Type Inference List(1, 2,3, 4).filter { a: Int => a > 1 }
  • 47.
    Type Inference List(1, 2,3, 4).filter { a: Int => a > 1 } List(1, 2, 3, 4).filter { a => a > 1 }
  • 48.
    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 }
  • 49.
    Limitation Of TypeInference 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)
  • 50.