by Yuriy Polyulya / @polyulya / +blog
is a programming language feature that allows one interface to be used for a general class of actions.
defined on functions or a data types so that they can be written generically and it can handle values identically without depending on their type.
class Container[A](val value : A) { // Type-constructor
def map[B](f : A => B): Container[B] =
new Container(f(value))
}
new Container(10) map { x => (x * 2).toString } // Container(“20”)
new Container("10") map { x => x.toInt * 2 } // Container(20)
Kind is the type of a type-constructor (type of of a higher-order type operator).
val x : String
val y : List[String]
def id(x : Int):Int = x
def map(f : Int=> Int, x: Int) = f(x)
type String :: *
type List :: * -> *
type Function1 :: * -> * -> *
type Id[A] = A
type Map[C[_],A] = C[A]
trait ~>[T[_],R[_]] { // T & R - have predicates [_]
def apply[A](a : T[A]): R[A]
}
val naturalTransformation = new (List ~> Option) { // ~>[List, Option]
def apply[A](a : List[A]): Option[A] = a.headOption
}
way of propagation types relation rules to type constructor relation rules.
Meaning | Scala notation | |
Covariant | C[T’] is a subclass of C[T] | [+T] |
Contravariant | C[T] is a subclass of C[T’] | [-T] |
Invariant | C[T] and C[T’] are not related | [T] |
way of propagation types relation rules to type constructor relation rules.
by using upper and lower bounds notations.
case class Employee(name: String)
case class Developer(yearsOfExperince: Int)
// and container:
class Foo[T]
def apply(a : Foo[_ <: Employee]): String
// or by existential type declaration:
def apply(a: Foo[X] forSome { type X <: Employee}): String
def apply[A](a: Foo[_ >: Employee]): String
// or by existential type declaration:
def apply(a : Foo[X] forSome { type X >: Employee}): String
is a form of type polymorphism in which a subtype is a data-type that is related to another data-type (the super-type) by some notion of substitutability.
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends Animal with HasLegs
class Cat extends Animal with Furry with FourLegged
val cat = new Cat()
trait RichIterable[A] {
def iterator : java.util.Iterator[A] // contract method
def foreach(f : A => Unit) = {
val iter = iterator
while (iter.hasNext) f(iter.next)
}
def foldLeft[B](seed : B)(f : (B, A) => B) = {
var result = seed
foreach(e => result = f(result, e))
result
}
}
val richSet =
new java.util.HashSet[Int]
with RichIterable[Int]
richSet.add(1)
richSet.add(2)
richSet.foldLeft(0)((x, y) => x + y) // == 3
trait IgnoreCaseSet extends java.util.Set[String] {
abstract override def add(e : String) = {
super.add(e.toLowerCase)
}
abstract override def contains(e: Object) = {
e match {
case s: String => super.contains(s.toLowerCase)
case o => super.contains(o)
}
}
}
val set =
new java.util.HashSet[String]
with IgnoreCaseSet
set.add("HI THERE") // uppercase
set.contains("hi there") // lowercase
//Facets
trait Entity { ... }
trait InventoryItemSet { ... }
trait PurchaseLimiter { ... }
trait MailNotifier { ... }
trait Versioned { ... }
trait Transactional { ... }
// Composition
val order = new Order(customer)
with Entity
with InventoryItemSet
with PurchaseLimiter
with MailNotifier
with Versioned
with Transactional
allows creating several methods with the same name which differ from each other in the type of the input and the output of the function.
case class Complex(re : Double, im : Double) {
def + (another : Complex) =
new Complex(re + another.re, im + another.im)
def unary_- =
new Complex(-re, -im)
}
Complex(2, 5) + Complex(1, -2) // == Complex(3, 3)
-Complex(1, -2) // == Complex(-1, 2)
is the operation of converting an argument or an operand to the type expected by a function or an operator.
case class Complex(re : Double, im : Double) {
def + (another : Complex) =
new Complex(re + another.re, im + another.im)
}
//implicit conversion: a form of coercion
implicit def doubleToComplex(d: Double) = Complex(d, 0)
Complex(2.0, 5.0) + 5.0 // == Complex(7.0, 5.0)
5.0 + Complex(1.0, -2.0) // == Complex(6.0, -2.0)
val str: String = "test - ok"
//C/C++ style condition expression:
if(str) println(str) //error: type mismatch
AnyRef to Boolean coversion needed
implicit def anyRefIsNotNull(x: AnyRef): Boolean = x != null
//C/C++ style condition expression:
if(str) println(str) //out: test - ok
Ternary operator similar to ? in C/C++:
val res : String = (4*4 > 14) ? "GT" | "LT or EQ"
Implicit coversion from Boolean to something with ? method:
implicit def boolToOperator(c : Boolean) = new {
def ?[A](t : => A) = new {
def |(f : => A) = if(c) t else f
}
}
Or:
class MakeIfTrue(b : => Boolean) {
def ?[A](t : => A) = new IfTrue[A](b,t)
}
class IfTrue[A](b : => Boolean, t : => A) {
def |(f : => A) = if (b) t else f
}
implicit def autoMakeIfTrue(b: => Boolean) = new MakeIfTrue(b)
def multiplier(i : Int)(implicit factor : Int) {
println(i * factor)
}
implicit val factor = 2
val res1 = multiplier(2) // res: 4
val res2 = multiplier(2)(3) // res: 6
is a type system construct that supports ad hoc polymorphism. This is achieved by adding constraints to type variables in parametrically polymorphic types.
class Show a where
show :: a -> String
trait Show[A] {
def shows(a: A): String
}
//OR Haskell notation:
trait Show[A] {
def shows : A => String
}
is a type system construct that supports ad hoc polymorphism. This is achieved by adding constraints to type variables in parametrically polymorphic types.
// Type-class Int instance
implicit val IntShow = new Show[Int] {
def shows(a : Int) = a.toString
}
//Type-class List instance:
implicit def ListShow[T] = new Show[List[T]] {
def shows(a : List[T]) = a.mkString(", ")
}
def shows[A](a : A)(implicit sh: Show[A]) = sh.shows(a)
//OR:
def shows[A : Show](a : A) = implicitly[Show[A]].shows(a)
// must have a "Show[Int]" instance in scope
shows(42)
is a type system construct that supports ad hoc polymorphism. This is achieved by adding constraints to type variables in parametrically polymorphic types.
trait ShowSyntax[A] {
def shows : String
}
implicit def toShowSyntax[A : Show](a : A)= new ShowSyntax[A] {
def shows = implicitly[Show[A]].shows(a)
}
// must have a "Show[Int]" instance in scope
42.shows
println(5.shows) // prints '5‘
localy {
implicit val AltIntShow = new Show[Int] {
def shows(i : Int) = (1 to i) map(_ => "|") mkString
}
println(5.shows) // prints '|||||'
}