Type Classes
def addToAll(
list: List[Int],
value: Int
): List[Int] =
list.map(x => x + value)
def addToAll(
list: List[String],
value: String
): List[String] =
list.map(x => x + value)
def addToAll[A](
list: List[A],
value: A
): List[A] =
list.map(x => x + value)
<console>:11: error: type mismatch;
found : A
required: String
trait Addable {
def add(other: ???)
}
trait Addable[A] {
def add(other: A): A
}
def addToAll[A <: Addable[A]](
list: List[A],
value: A
): List[A] =
list.map(x => x.add(value))
case class Vector2D(
x: Int,
y: Int
) extends Addable[Vector2D] {
override def add(other: Vector2D) =
Vector2D(
this.x + other.x,
this.y + other.y
)
}
scala> addToAll(
List(
Vector2D(3, 4),
Vector2D(5, 6)
),
Vector2D(1, 2)
)
res1: List[Vector2D] =
List(Vector2D(4,6), Vector2D(6,8))
trait Addable[A] {
def add(first: A, second: A): A
}
case class Vector2D(x: Int, y: Int)
val vector2DAddable: Addable[Vector2D] =
new Addable[Vector2D] {
override def add(
first: Vector2D,
second: Vector2D
): Vector2D =
Vector2D(
first.x + second.x,
first.y + second.y
)
}
def addToAll[A](
list: List[A],
value: A,
addable: Addable[A]
): List[A] =
list.map(x => addable.add(x, value))
scala> addToAll(
List(
Vector2D(3, 4),
Vector2D(5, 6)
),
Vector2D(1, 2),
vector2Daddable
)
res2: List[Vector2D] =
List(Vector2D(4,6), Vector2D(6,8))
val intAddable: Addable[Int] =
new Addable[Int] {
override def add(
first: Int,
second: Int
): Int =
first + second
}
scala> addToAll(
List(1, 2), 3, intAddable
)
res3: List[Int] = List(4, 5)
def addToAll[A](
list: List[A],
value: A
)(
implicit addable: Addable[A]
): List[A] =
list.map(x => addable.add(x, value))
implicit val intAddable: Addable[Int] =
...
scala> addToAll(List(1, 2), 3)
res4: List[Int] = List(4, 5)
scala> addToAll(
List(1, 2), 3
)(intAddable)
res5: List[Int] = List(4, 5)
Type Classes
trait Addable[A] {
def add(first: A, second: A): A
}
Класс типов
Экземпляры класса типов
Интерфейсы с использованием неявных параметров
Интерфейсы с использованием обогащающих классов и неявных параметров
intAddable.add(0, 1)
vectorAddable.add(
Vector2D(0, 1),
Vector2D(2, 3)
)
Интерфейсы с неявными параметрами
object Addable {
def add[A](
first: A,
second: A
)(
implicit addable: Addable[A]
): A =
addable.add(first, second)
}
Addable.add(0, 1)
Addable.add(
Vector2D(0, 1),
Vector2D(2, 3)
)
def addToAll[A: Addable](
list: List[A],
value: A
): List[A] =
list.map(x => Addable.add(x, value))
Обогащающие классы
implicit class AddableOps[A](value: A) {
def +(
other: A
)(
implicit addable: Addable[A]
): A =
addable.add(value, other)
}
scala> Vector2D(1, 2) + Vector2D(3, 4)
res1: Vector2D = Vector2D(4,6)
def addToAll[A: Addable](
list: List[A],
value: A
): List[A] =
list.map(x => x + value)
Конструкторы типов
implicit def lAdd[A]: Addable[List[A]] =
new Addable[List[A]] {
override def add(
first: List[A],
second: List[A]
): List[A] =
first ++ second
}
scala> List(1, 2) + List(3, 4, 5)
res0: List[Int] = List(1, 2, 3, 4, 5)
implicit def oa[A : Addable]
: Addable[Option[A]] =
new Addable[Option[A]] {
override def add(
first: Option[A],
second: Option[A]
): Option[A] =
first.fold(second){ x =>
second.fold(first){ y =>
Some(x + y)
}
}
}
scala> Option(3) + Option(5)
res1: Option[Int] = Some(8)
Отношения типов
trait Convertible[A, B] {
def convert(value: A) : B
}
object Convertible {
def convert[A, B](
value: A
)(
implicit cv: Convertible[A, B]
): B =
convertible.convert(value)
}
implicit def optionListCV[A]
: Convertible[Option[A], List[A]] =
new Convertible[Option[A], List[A]] {
override def convert(
value: Option[A]
): List[A] =
value.fold(List.empty[A])(List(_)) }
scala> Convertible.convert(Option(5))
res0: List[Int] = List(5)
Вариантность типовых параметров
scala> Convertible.convert(Some(3))
<console>:15: error: could not find implicit value for parameter convertible: Convertible[Some[Int],B]
trait Convertible[-A, +B] {
def convert(value: A): B
}
scala> Convertible.convert(Some(3))
res0: List[Int] = List(3)
Реализация методов
trait Eq[A] {
def eqv(a: A, b: A): Boolean =
!neqv(a, b)
def neqv(a: A, b: A): Boolean =
!eqv(a, b)
}
implicit val intEq: Eq[Int] =
new Eq[Int] {
override def eqv(
a: Int,
b: Int
): Boolean =
first == second
}
scala> intEq.neqv(0, 1)
res1: Boolean = true
Наследование
trait Ordered[A] extends Eq[A] {
def order(first: A, second: A): Int
override def eqv(
first: A,
second: A
): Boolean =
order(first, second) == 0
}
Классы типов для конструкторов типов
trait Functor[F[_]]{
def fmap[A, B](
value : F[A],
func: A => B
): F[B]
}
implicit val lFunc: Functor[List] =
new Functor[List] {
override def fmap[A, B](
value: List[A],
func: A => B
): List[B] =
value.map(func)
}
scala> def add1(x: Int) = x + 1
scala> lFunc.fmap(List(1, 2), add1)
res1: List[Int] = List(2, 3)
Автоматическая генерация экземпляров
case class A(a: Int, b: String)
case class B(a: A, b: List[Int])
import io.circe.generic.semiauto._ implicit val aEncoder: Encoder[A] =
deriveEncoder[A]
implicit val bEncoder: Encoder[B] =
deriveEncoder[B]
scala> bEncoder(
B(A(1, "a"), List(3, 2))
).spaces2
res0: String =
{
"a" : {
"a" : 1,
"b" : "a"
},
"b" : [3, 2]
}