Download - Types by Adform Research
Picking up from session 1
OOP/modular
• named BitSet
• compound Channel with Logged
• refined Channel {def close(): Unit }
Functional
• parametrized List[Int]
• existential List[T] forSome {type T}
• higher-kinded List
Picking up from session 1
• Self types
• Type members
• Implicits
• Type classes
• Logic programming with types
• Shapeless
Reminder
Self types
trait S3Connector { this: S3Processor =>
val accessKey = "fadsf76asf576dsa"
// ...
}
Limits where you can mix this trait in, i.e., it will only work like this:
class S3App extends S3Processor with S3Connector {
// ...
}
Type membersAlternative to type parameters, i.e.:
trait StringCollection extends Collection[String] {
// ...
}
vs
trait StringCollection extends Collection {
type T = String
// ...
}
Can also be
• abstract, so you could remove the String part as well
• type bounded, e.g., type T <: java.io.Closeable
Type members
Mostly useful for dealing with covariance and mixin problems:
class Animal {
def eat(f: Food)
}
class Cow extends Animal {
// cows only eat grass!
}
Type members
Mostly useful for dealing with covariance problems:
class Animal {
type SuitableFood
def eat(f: SuitableFood)
}
class Cow extends Animal {
type SuitableFood = Grass
}
Type membersCan be helpful in reducing redundancy for mixins:
trait IntTreeHelper { this: Tree[Int] =>
// ...
}
class TreeProcessor extends Tree[Int] with IntTreeHelper =>
// ...
}
vs
trait IntTreeHelper { this: Tree =>
type T = Int
// ...
}
class TreeProcessor extends Tree with IntTreeHelper =>
// ...
}
Implicits
Invisible entities within current scope:
• Parameters
• Methods
• Classes
Can be quite sneaky so don’t overdo it!
Implicit parameters
• Only one implicit keyword in signature
• Should come last
• Generally good for hiding configuration objects
implicit val arg1 = 1
implicit val arg2 = 2L
def ghostprint(a: Int)(implicit b: Int, c: Long) = println(a,b,c)
Implicit parameters
abstract class JobWithConf(args: Args) extends Job(args) {
implicit val configurationOfHadoop = …
}
class MyJob(args: Args) extends JobWithConf(args) {
…
val folderContents = getHDFSContents(folder)
}
object HDFSUtil{
def getHDFSContents(in: String)(implicit configuration: Configuration): List[String] =
Option(FileSystem.get(configuration).globStatus(new Path(in))) match {
case Some(a) => a.toList
case None => Nil
}.map(_.getPath.getName)
}
Implicit parameters
Helpful predefined function is implicitly[T], which ”catches” an implicit:
def implicitly[T](implicit e: T): T = e
Actually, configurationOfHadoop from the previous slide is defined as
@transient implicit val configurationOfHadoop = implicitly[Mode] match {
case Hdfs(_, configuration) => configuration
case _ => sys.error("Not running on Hadoop!")
}
Implicit methods
Usually used to silently convert between types
class MyInt(val i: Int) {
def inc = new MyInt(i+1)
}
implicit def int2MyInt(i: Int) = new MyInt(i)
5.inc
Implicit classes
Same idea, used to “pimp your classes”:
implicit class MyInt(val i: Int) {
def inc = new MyInt(i+1)
}
5.inc
Implicit classes
You can also pimp companion objects:
implicit class SuperList(companion: List.type) {
def fillInterspersed[A](n: Int)(a: A, b: A) = …
}
View and context bounds
These are shortcuts for providing implicit conversions and wrappers:
// view bound
def f[A <% B](a: A) = a.bMethod
is equal todef f[A](a: A)(implicit ev: A => B) = a.bMethod
// context bound
def g[A : B](a: A) = h(a)
is equal todef g[A](a: A)(implicit ev: B[A]) = h(a)
Type classes
• come from Haskell
• no relation to OOP classes
• a facility for ad-hoc polymorphism (compile-time variant of subtype p-m)
• in Scala they are basically traits + implicits (context bounds)
Type classes
trait Monoid[T] {
def zero: T
def append(a: T, b: T): T
}
implicit object IntIsMonoid extends Monoid[Int] {
def zero = 0
def append(a: Int, b: Int) = a + b
}
implicit object StringIsMonoid extends Monoid[String] {
def zero = ""
def append(a: String, b: String) = a concat b // try to avoid + for Strings
}
Type classes
def sum[T](xs: List[T])(implicit monoid: Monoid[T]) =
xs.foldLeft(monoid.zero)(monoid.append)
//context bounds
def sum[T : Monoid](xs: List[T]) = {
val monoid = implicitly[Monoid[T]]
xs.foldLeft(monoid.zero)(monoid.append)
}
Logic programming
• imperative - state = functional
• functional - DFS = logic
• Predicates/constraints instead of functions:
pow(2,3) = 8 ~ pow(2,3,8)
• Essentially an interface to a theorem prover
Prolog
• created in early 70s
• archetypical LP language
• Towers of Hanoi:
move(1, L, R, C) :-
write(['Move top disk from ', L , ' to ', R]),
nl.
move(N, L, R, C) :-
N > 1,
M is N - 1,
move(M, L, C, R),
move(1, L, R, C),
move(M, C, R, L).
move(3, L, R, C).
Logic programming
Scala implicit search engine is sort of a prover:
• facts = implicit vals
• rules = implicit defs
Towers of Hanoi
sealed trait Name
case object Left extends Name
case object Right extends Name
case object Center extends Name
trait Move[N <: Nat, L <: Name, R <: Name, C <: Name]
Towers of Hanoi
implicit def move0[L <: Name,
R <: Name,
C <: Name]: Move[_1, L, R, C] =
new Move[_1, L, R, C] {}
Towers of Hanoi
implicit def move1[N <: Nat,
M <: Nat,
L <: Name,
R <: Name,
C <: Name]
(implicit ev0 : LT[_1, N],
ev1 : Diff.Aux[N, _1, M],
ev2 : Move[M, L, C, R],
ev3 : Move[_1, L, R, C],
ev4 : Move[M, C, R, L]): Move[N, L, R, C] =
new Move[N, L, R, C] {}
Towers of Hanoi
implicitly[Move[_3, Left.type, Right.type, Center.type]]
Shapeless
• So what are these Nat, LT, Diff.Aux things?
• These are type-level numbers and arithmetical operations defined in a library called Shapeless:
sealed trait Nat
sealed trait _0 extends Nat
sealed trait Succ[N <: Nat] extends Nat
type _1 = Succ[_0]
type _2 = Succ[_1]
...
trait LT[A <: Nat, B <: Nat]
object LT {
def apply[A <: Nat, B <: Nat](implicit lt: A < B): LT[A, B] = lt
type <[A <: Nat, B <: Nat] = LT[A, B]
implicit def lt1[B <: Nat] = new <[_0, Succ[B]] {}
implicit def lt2[A <: Nat, B <: Nat](implicit lt : A < B) = new <[Succ[A], Succ[B]] {}
}
Shapeless
• HLists: like tuples, but with variable arity and collection functionality
• (also can be created from case classes)
• HMap: bidirectional map, e.g. HMap[BiMapIS](23 -> "foo", "bar" -> 13)
• Coproducts: Like Either, but with arbitrary number of choices
• Lens: compositional getters and setters for nested case classes
• collections with statically known sizes
• typesafe casting (wrapping in an Option)
References
• http://debasishg.blogspot.com/2010/02/scala-self-type-annotations-for.html
• http://stackoverflow.com/questions/1154571/scala-abstract-types-vs-generics
• http://like-a-boss.net/2013/03/29/polymorphism-and-typeclasses-in-scala.html
• http://lanyrd.com/2014/scalaio-fr/sdgcwm
• https://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala
• https://github.com/milessabin/shapeless/wiki/Feature-overview%3a-shapeless-2.0.0