--- /dev/null
+object Main {
+
+ // Question 1.
+ trait Stack[+A] {
+ def push[B >: A](elem: B): Stack[B]
+ def top: A
+ def pop: Stack[A]
+ }
+
+ case class EmptyStack[A]() extends Stack[A] {
+ def push[B >: A](elem: B): Stack[B] = ElemStack(elem, this)
+ def top: A = throw new Exception
+ def pop: Stack[A] = throw new Exception
+ }
+
+ case class ElemStack[A](top: A, rest: Stack[A]) extends Stack[A] {
+ def push[B >: A](elem: B): Stack[B] = ElemStack(elem, this)
+ def pop: Stack[A] = rest
+ override def toString = s"${top.toString()},${rest.toString()}"
+ }
+
+ def question1 = {
+ println("Question 1")
+ // Construction, pop and toString.
+ val a = EmptyStack().push("hello").push("world").push("it's fun").pop
+ println(s"a: ${a}")
+ assert(a.toString() == "world,hello,EmptyStack()")
+
+ // Getting top.
+ val b = EmptyStack().push(1).push(3)
+ println(s"b.top: ${b.top}")
+ assert(b.top == 3)
+
+ // Variance checks.
+ class Foo
+ class Bar extends Foo
+ val c: Stack[Bar] = EmptyStack().push(new Bar()).push(new Bar())
+ assert(c.top.isInstanceOf[Bar])
+ assert(c.top.isInstanceOf[Foo])
+
+ // Variance check 2.
+ val d: Stack[Foo] = EmptyStack().push(new Bar()).push(new Bar())
+ assert(d.top.isInstanceOf[Foo])
+ }
+
+ // Question 2.
+ abstract sealed class Temperature {
+ val temp: Double
+ }
+
+ case class Celsius(v: Double) extends Temperature {
+ val temp = v
+ override def toString = s"$v° C"
+ }
+
+ case class Kelvin(v: Double) extends Temperature {
+ val temp = v
+ override def toString = s"$v° K"
+ }
+
+ object Temperature {
+ implicit def toCelsius(temp: Kelvin): Celsius = new Celsius(temp.v + 273.15)
+ implicit def toCelsius(temp: Int): Celsius = new Celsius(temp)
+ implicit def toKelvin(temp: Celsius): Kelvin = new Kelvin(temp.v - 273.15)
+ implicit def toKelvin(temp: Int): Kelvin = new Kelvin(temp)
+ }
+
+ def question2 = {
+ println("Question 2")
+ val a: Celsius = 30
+ val b: Kelvin = 30
+ val c: Kelvin = Celsius(10)
+ val d: Celsius = c
+ val e: Temperature = d
+ println(a)
+ println(b)
+ println(e)
+ }
+
+ // Question 3.
+ def addStream(s1: Stream[Int], s2: Stream[Int]): Stream[Int] =
+ (s1.head + s2.head) #:: addStream(s1.tail, s2.tail)
+
+ lazy val fib: Stream[Int] = 0 #:: 1 #:: addStream(fib, fib.tail)
+
+ def question3 = {
+ println(fib.take(30).toList)
+ }
+
+ def main(args: Array[String]): Unit = {
+ question1
+ question2
+ question3
+ }
+}