From dbd68385c7e4873046f83a8c65cb4a028da8306e Mon Sep 17 00:00:00 2001 From: dbykov-naumen Date: Wed, 7 Apr 2021 17:38:55 +0500 Subject: [PATCH] add lecture5 + HW5 --- homeworks/homework_5/build.sbt | 9 + homeworks/homework_5/homework_5.md | 39 + homeworks/homework_5/project/build.properties | 1 + .../homework_5/src/main/scala/Exercises.scala | 23 + .../homework_5/src/test/scala/Test.scala | 58 + index.md | 4 +- lectures/scala_lecture_5.html | 1743 +++++++++++++++++ 7 files changed, 1876 insertions(+), 1 deletion(-) create mode 100644 homeworks/homework_5/build.sbt create mode 100644 homeworks/homework_5/homework_5.md create mode 100644 homeworks/homework_5/project/build.properties create mode 100644 homeworks/homework_5/src/main/scala/Exercises.scala create mode 100644 homeworks/homework_5/src/test/scala/Test.scala create mode 100644 lectures/scala_lecture_5.html diff --git a/homeworks/homework_5/build.sbt b/homeworks/homework_5/build.sbt new file mode 100644 index 00000000..a5844cf4 --- /dev/null +++ b/homeworks/homework_5/build.sbt @@ -0,0 +1,9 @@ +name := "homework_5" + +version := "0.1" + +scalaVersion := "2.12.10" + +libraryDependencies += "com.lihaoyi" %% "utest" % "0.5.3" % "test" + +testFrameworks += new TestFramework("utest.runner.Framework") diff --git a/homeworks/homework_5/homework_5.md b/homeworks/homework_5/homework_5.md new file mode 100644 index 00000000..f32b2d7d --- /dev/null +++ b/homeworks/homework_5/homework_5.md @@ -0,0 +1,39 @@ +# Домашнее задание №5 (курс Scala, Naumen) + +## Задание 1 + +Реализуйте класс приюта для животных (`case class Shelter...`), который хранит список (`List`) +животных, которые в нем содержатся. Приют должен быть параметризован типом содержащихся животных. +Например, `Shelter[Cat]` может содержать только котов, а `Shelter[Animal]` любых животных.
+Реализуйте метод `+`, который добавляет в приют животное (метод должен вернуть новый приют, а не модифицировать старый). +Тип полученного приюта должен остаться настолько точным, насколько это возможным. +Например, если в приют с котами добавляют кота, то в результате должен снова получиться приют с котами.
+`val s: Shelter[Cat] = Shelter(List(Cat("Garfield"))) + Cat("Kuzya")`
+Если же в приют с котами добавить собаку, то получится приют с животными.
+`val s: Shelter[Animal] = Shelter(List(Cat("Garfield"))) + Dog("Goofy")`
+Реализовать метод `++`, который производит сложение двух приютов и возвращает приют, +в котором содержатся животные из обоих, участвовавших в сложении. +Тип результата точно так же должен быть настолько точным, насколько это возможно, исходя из +типов приютов, участвовавших в сложении.
+Реализовать метод `getNames`, который возвращает спсок имен животных, содержащихся в приюте.
+Тесты должны компилироваться и успешно проходить. Обратите внимание, что до вполнения второго +задания соответствующая часть тестов не будет компилироваться, так что ее можно закомментировать. + +## Задание 2 + +Реализовать тип еда (`trait Food`), который параметризован типом животных, которые +ее могут есть. Например, `Food[Cat]` могут есть только коты, а +`Food[Animal]` могут есть любые животные. Реализовать объекты (`case object`):
+`Meat` - мясо, могут есть все животные.
+`Milk` - молоко, могут есть коты.
+`Bread` - хлеб, могут есть собаки.
+Определить для еды метод `feed`, который принимает животное того типа, которым параметризована эта еда, +и возвращает строку, содержащую "<имя животного> eats <название еды со строчной буквы>". Например, +`Meat.feed(Cat("Garfield"))` должно вернуть "Garfield eats meat", а `Milk.feed(Dog("Goofy"))` не должно +компилироваться т.к. собаки не едят молоко. +Реализовать для приюта метод, который получает на вход еду, которая подходит для +содержащихся в нем животных, и выводит список результатов применеия метода `Food.feed` ко +всем животным. Обратите внамение, что `Shelter(List(Cat("Garfield"), Dog("Goofy"))).feed(Milk)` не должно +компилироваться т.к. в этом приюте содержатся и кошки и собаки, +следовательно тип этого приюта - `Shelter[Animal]`, а произвольные животные не могут есть молоко. Единственная +еда, которой их можно было бы покормить - мясо. diff --git a/homeworks/homework_5/project/build.properties b/homeworks/homework_5/project/build.properties new file mode 100644 index 00000000..010613d5 --- /dev/null +++ b/homeworks/homework_5/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.3.3 \ No newline at end of file diff --git a/homeworks/homework_5/src/main/scala/Exercises.scala b/homeworks/homework_5/src/main/scala/Exercises.scala new file mode 100644 index 00000000..2be7d30e --- /dev/null +++ b/homeworks/homework_5/src/main/scala/Exercises.scala @@ -0,0 +1,23 @@ +object Exercises { + trait Animal { + def name: String + } + + case class Cat(override val name: String) extends Animal + + case class Dog(override val name: String) extends Animal + + + + case class Shelter ... + + + + trait Food ... + + case object Meat extends Food[Animal] ... + + case object Milk extends Food[Cat] ... + + case object Bread extends Food[Dog] ... +} diff --git a/homeworks/homework_5/src/test/scala/Test.scala b/homeworks/homework_5/src/test/scala/Test.scala new file mode 100644 index 00000000..f8e61f8f --- /dev/null +++ b/homeworks/homework_5/src/test/scala/Test.scala @@ -0,0 +1,58 @@ +import Exercises._ +import utest._ + +object Test extends TestSuite { + + val tests = Tests { + + //Test task 1 + + val s1: Shelter[Cat] = Shelter(List(Cat("Garfield"))) + + val s2: Shelter[Dog] = Shelter(List(Dog("Goofy"))) + + val s3: Shelter[Animal] = s1 ++ s2 + + val s4 = s3 + Cat("Kuzya") + + assert(s4.getNames.toSet == Set("Garfield", "Goofy", "Kuzya")) + + val s5: Shelter[Animal] = s1 + Dog("Barbos") + + assert(s5.getNames.toSet == Set("Garfield", "Barbos")) + + val s6: Shelter[Dog] = s2 + Dog("Barbos") + + assert(s6.getNames.toSet == Set("Goofy", "Barbos")) + + //Test task 2 + + val r1 = s1.feed(Meat) + + assert(r1.toSet == Set("Garfield eats meat")) + + val r2 = s1.feed(Milk) + + assert(r2.toSet == Set("Garfield eats milk")) + + val r3 = s2.feed(Meat) + + assert(r3.toSet == Set("Goofy eats meat")) + + val r4 = s2.feed(Bread) + + assert(r4.toSet == Set("Goofy eats bread")) + + val r5 = s4.feed(Meat) + + assert( + r5.toSet == Set( + "Garfield eats meat", + "Goofy eats meat", + "Kuzya eats meat" + ) + ) + + } + +} diff --git a/index.md b/index.md index 78544742..6ba56539 100644 --- a/index.md +++ b/index.md @@ -9,10 +9,12 @@ layout: default * Лекция 2. [Программирование на Scala. Базовые концепции.](lectures/scala_lecture_2.html) * Лекция 3. [Система типов в Scala](lectures/scala_lecture_3.html) * Лекция 4. [Классы и объекты в Scala.(PowerPoint)](lectures/scala_lecture_4.pptx) +* Лекция 5. [Параметрический полиморфизм в Scala](lectures/scala_lecture_5.html) ### Домашние задания * [Инструкция по предоставлению результатов](https://github.com/naumen-student/naumen.scala.course.2021.spring#%D0%BF%D1%80%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%BE%D0%B2) * [Задание 1](homeworks/homework_1/homework_1.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2021.spring/tree/master/homeworks/homework_1) * [Задание 2](homeworks/homework_2/homework_2.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2021.spring/tree/master/homeworks/homework_2) * [Задание 3](homeworks/homework_3/homework_3.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2021.spring/tree/master/homeworks/homework_3) -* [Задание 4](homeworks/homework_4/homework_4.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2021.spring/tree/master/homeworks/homework_4) \ No newline at end of file +* [Задание 4](homeworks/homework_4/homework_4.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2021.spring/tree/master/homeworks/homework_4) +* [Задание 5](homeworks/homework_5/homework_5.md) [папка в GitHub](https://github.com/naumen-student/naumen.scala.course.2021.spring/tree/master/homeworks/homework_5) \ No newline at end of file diff --git a/lectures/scala_lecture_5.html b/lectures/scala_lecture_5.html new file mode 100644 index 00000000..58b1594c --- /dev/null +++ b/lectures/scala_lecture_5.html @@ -0,0 +1,1743 @@ + + + + + + + Параметрический полиморфизм в Scala + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+

Параметрический полиморфизм в Scala

+
+ + +
+

IntBox

+
+
+
+
+ + case class IntBox(
+     value: Int
+ )
+
+ scala> IntBox(5)
+ res0: IntBox = IntBox(5)
+
+
+
+
+
+ +
+

StringBox

+
+
+
+
+ + case class StringBox(
+     value: String
+ )
+
+ scala> StringBox("hello")
+ res0: StringBox = StringBox(hello)
+
+
+
+
+
+ +
+

BooleanBox

+
+
+
+
+ + case class BooleanBox(
+     value: Boolean
+ )
+
+ scala> BooleanBox(true)
+ res1: BooleanBox = BooleanBox(true)
+
+
+
+
+
+ + +
+

...

+
+ + +
+

Box[T]

+
+
+
+
+ + case class Box[T](
+     value: T
+ )
+
+ scala> Box[Int](5)
+ res2: Box[Int] = Box(5)
+
+ scala> Box[String]("hello")
+ res3: Box[String] = Box(hello)
+
+
+
+
+
+ +
+
+
+
+ + scala> Box[String](5)
+
+ <console>:14: error: type mismatch;
+  found   : Int(5)
+  required: String
+
+
+
+
+
+ +
+
+
+
+ + scala> Box(5)
+ res1: Box[Int] = Box(5)
+
+ scala> Box("hello")
+ res2: Box[String] = Box(hello)
+
+
+
+
+
+ + +
+
+
+
+ + scala> val x = Box(5)
+ x: Box[Int] = Box(5)
+
+ scala> x.value
+ res3: Int = 5
+
+ scala> x.value + 1
+ res4: Int = 6
+
+ scala> x.value.substring(1)
+ <console>:15: error: value substring is not a member of Int +
+
+
+
+
+ +
+
+
+
+ + Option[T] - 0 или 1 элемент типа Т
+
+ List[T] - список значений типа T +
+
+
+
+
+ + +
+

Несколько параметров

+
+
+
+
+ + case class Box2[A, B](
+     value1: A,
+     value2: B
+ )
+
+ scala> Box2(5, "hello")
+ res6: Box2[Int,String] = Box2(5,hello) +
+
+
+
+
+ +
+
+
+
+ + (A, B) - пара элементов типов A и B
+
+ Either[A, B] - либо значение типа A, либо значение типа B
+
+ Map[K, V] - отображение ключ (K) - значение (V)
+
+ A => B - функция из объекта типа A в объект типа B +
+
+
+
+
+ +
+
+
+
+ + Box(0.5): ???
+
+ Box2(true, "hello"): ???
+
+ Box(Box(5)): ???
+
+ Box(Box2(0.5, true)): ???
+
+ Box2(Box(3), Box2("hello", true)): ??? +
+
+
+
+
+ +
+
+
+
+ + Box(0.5): Box[Double]
+
+ Box2(true, "hello"): ???
+
+ Box(Box(5)): ???
+
+ Box(Box2(0.5, true)): ???
+
+ Box2(Box(3), Box2("hello", true)): ??? +
+
+
+
+
+ +
+
+
+
+ + Box(0.5): Box[Double]
+
+ Box2(true, "hello"): Box2[Boolean,String]
+
+ Box(Box(5)): ???
+
+ Box(Box2(0.5, true)): ???
+
+ Box2(Box(3), Box2("hello", true)): ??? +
+
+
+
+
+ +
+
+
+
+ + Box(0.5): Box[Double]
+
+ Box2(true, "hello"): Box2[Boolean,String]
+
+ Box(Box(5)): Box[Box[Int]]
+
+ Box(Box2(0.5, true)): ???
+
+ Box2(Box(3), Box2("hello", true)): ??? +
+
+
+
+
+ +
+
+
+
+ + Box(0.5): Box[Double]
+
+ Box2(true, "hello"): Box2[Boolean,String]
+
+ Box(Box(5)): Box[Box[Int]]
+
+ Box(Box2(0.5, true)):
  Box[Box2[Double,Boolean]]
+
+ Box2(Box(3), Box2("hello", true)): ??? +
+
+
+
+
+ +
+
+
+
+ + Box(0.5): Box[Double]
+
+ Box2(true, "hello"): Box2[Boolean,String]
+
+ Box(Box(5)): Box[Box[Int]]
+
+ Box(Box2(0.5, true)):
  Box[Box2[Double,Boolean]]
+
+ Box2(Box(3), Box2("hello", true)):
  Box2[Box[Int],Box2[String,Boolean]] +
+
+
+
+
+ + +
+

Параметры функций

+
+
+
+
+ + def toBox2[A, B](
    first: Box[A],
    second: Box[B]
): Box2[A, B] =
+   Box2(first.value, second.value)
+
+ scala> toBox2(Box("hello"), Box(5))
+ res7: Box2[String,Int] = Box2(hello,5) +
+
+
+
+
+ + +
+

Область видимости

+
+
+
+
+ + case class Box2[A, B](
    value1: A,
    value2: B
) {
+   def withNewValue1(
      newValue: A
  ): Box2[A, B] =
+     Box2(newValue, value2)
+ }
+
+
+
+
+
+ + +
+
+
+
+ + case class Box2[A, B](
    value1: A,
    value2: B
) {
+   def withNewValue1[C](
      newValue: C
  ): Box2[C, B] =
+     Box2(newValue, value2)
+ }
+
+
+
+
+
+ + +
+

Конкретный параметр

+
+
+
+
+ + def sum(box: Box2[Int, Int]): Box[Int] =
+   Box(box.value1 + box.value2)
+
+ scala> sum(Box2(1, 2))
+ res8: Box[Int] = Box(3) +
+
+
+
+
+ + +
+

Игнорируем параметр

+
+
+
+
+ + def getFirst[T](box: Box2[T, _]): T =
  box.value1
+
+ scala> getFirst(Box2("hello", 4))
+ res9: String = hello +
+
+
+
+
+ +
+ Написать функцию swap, которая принимает пару (Box2) и возвращает ее с переставленными местами + элементами
+
+ ??? +
+ +
+
+
+
+ + def swap[A, B](
    box: Box2[A, B]
): Box2[B, A] =
  Box2(box.value2, box.value1)
+
+ scala> swap(Box2("hello", 5))
+ res0: Box2[Int,String] = Box2(5,hello) +
+
+
+
+
+ +
+ Написать функцию sum2, которая принимает две пары, у которых первые элементы Double-ы, и возвращает их сумму
+
+ ??? +
+ +
+
+
+
+ + def sum2(
    box1: Box2[Double, _]
    box2: Box2[Double, _]
): Double = +
  box1.value1 + box2.value1
+
+ scala> sum2(
  Box2(0.5, "hello"), Box2(0.7, true)
)
+ res2: Double = 1.2 +
+
+
+
+
+ +
+ Написать метод класса Box - map, который принимает функцию и возвращает новый Box с результатом применения + этой функции к хранящемуся в старом Box значению
+
+ ??? +
+ +
+
+
+
+ + case class Box[A](value: A) {
+   def map[B](f: A => B): Box[B] =
+     Box(f(value))
+ }
+
+ scala> Box("hello").map(_.length)
+ res3: Box[Int] = Box(5) +
+
+
+
+
+ + +
+

Upper Bounds

+
+ +
+
+
+
+ + trait Animal {
+   def name: String
+ }
+
+ case class Cat(
    override val name: String
) extends Animal
+
+ case class Dog(
    override val name: String
) extends Animal +
+
+
+
+
+ +
+
+
+
+ + def shortestName(
    animals: List[Animal]
): Option[Animal] =
+   animals.sortBy(_.name.length).headOption +
+
+
+
+
+ +
+
+
+
+ + def sayMeow(cat: Cat): String =
+   cat.name + " says meow."
+
+ val cats: List[Cat] = List(
  Cat("Garfield"),
  Cat("Lucy"),
  Cat("Kuzya")
)
+
+
+
+
+
+ +
+
+
+
+ + val cats: List[Cat] = ...
+
+ scala> val s = shortestName(cats)
+ s: Option[Animal] = Some(Cat(Lucy))
+
+ scala> s.map(cat => sayMeow(cat))
+ <console>:19: error: type mismatch;
+  found   : Animal
+  required: Cat +
+
+
+
+
+ +
+
+
+
+ + scala> s.map { cat =>
  sayMeow(cat.asInstanceOf[Cat])
}
+
+ res7: Option[String] =
  Some(Lucy says meow.)
+
+
+
+
+
+ Грязный код.
+ Потенциальные ошибки. +
+ +
+
+
+
+ + scala> val s =
  shortestName(List(Dog("Goofy")))
+ s: Option[Animal] = Some(Dog(Goofy))
+
+ scala> s.map { cat =>
  sayMeow(cat.asInstanceOf[Cat])
}
+
+ java.lang.ClassCastException:
  Dog cannot be cast to Cat
+
+
+
+
+
+ Ошибка во время исполнения. +
+ +
+
+
+
+ + def shortestName[T](
    animals: List[T]
): Option[T] =
+   animals.sortBy(_.name.length).headOption
+
+ <console>:11: error: value name is not a member of type parameter T +
+
+
+
+
+ + +
+

Upper Bound

+
+
+
+
+ + def shortestName[T <: Animal](
    animals: List[T]
): Option[T] =
+   animals.sortBy(_.name.length).headOption
+
+
+
+
+
+ +
+
+
+
+ + scala> val s = shortestName(cats)
+ s: Option[Cat] = Some(Cat(Lucy))
+
+ scala> s.map(sayMeow)
+ res11: Option[String] =
  Some(Lucy says meow.)
+
+
+
+
+
+ +
+
+
+
+ + scala> val s =
  shortestName(List(Dog("Goofy")))
+ s: Option[Dog] = Some(Dog(Goofy))
+
+ scala> s.map(sayMeow)
+ <console>:20: error: type mismatch;
+  found   : Cat => String
+  required: Dog => ? +
+
+
+
+
+ +
+

Lower Bounds

+
+ +
+
+
+
+ + def addToCats(
    cats: List[Cat],
    other: List[Animal]
): List[Animal] =
+   cats ++ other
+
+ scala> addToCats(
  List(Cat("Lucy")),
  List(Cat("Kuzya"))
)
+ res13: List[Animal] =
  List(Cat(Lucy), Cat(Kuzya))
+
+
+
+
+
+ +
+
+
+
+ + def addToCats[T](
    cats: List[Cat],
    other: List[T]
): List[T] =
+   cats ++ other
+
+ <console>:15: error: type mismatch;
+  found   : List[Any]
+  required: List[T] +
+
+
+
+
+ + +
+

Lower Bound

+
+
+
+
+ + def addToCats[T >: Cat](
    cats: List[Cat],
    other: List[T]
): List[T] =
+   cats ++ other +
+
+
+
+
+ +
+
+
+
+ + scala> addToCats(
  List(Cat("Lucy")),
  List(Cat("Kuzya"))
)
+
+ res8: List[Cat] =
  List(Cat(Lucy), Cat(Kuzya)) +
+
+
+
+
+ +
+
+
+
+ + scala> addToCats(
  List(Cat("Lucy")),
  List(Dog("Goofy"))
)
+
+ res9: List[Animal] =
  List(Cat(Lucy), Dog(Goofy)) +
+
+
+
+
+ + +
+

Type Class
(в следующих лекциях)

+
+
+
+
+ + def func[T: TypeClass] = ... + +
+
+
+
+ +
+

Несколько ограничений одновременно

+
+
+
+
+ + def func[T >: Cat <: Animal with CanMove: Decoder: Encoder] = ... + +
+
+
+
+ +
+

Variance

+
+ + +
+
+
+
+ + def getName(box: Box[Animal]): String =
  box.value.name
+
+ scala> val box = Box(Cat("Kuzya"))
+ box: Box[Cat] = Box(Cat(Kuzya))
+
+
+
+
+
+ +
+
+
+
+ + scala> getName(box)
+ <console>:19: error: type mismatch;
+  found   : Box[Cat]
+  required: Box[Animal]
+ Note: Cat <: Animal, but class Box is invariant in type T.
+
+
+
+
+
+ + +
+

Invariance

+
+ A =:= B => F[A] =:= F[B]
+
+
+
+
+ + case class Box[T](value: T)
+
+ Box[Animal] =:= Box[Animal]
+
+ Box[Cat] =:= Box[Cat]
+
+ Box[Cat] <: Box[Animal] - неверно! +
+
+
+
+
+ + +
+

Covariance

+
+ A <: B => F[A] <: F[B]
+
+
+
+
+ + case class Box[+T](value: T)
+
+ Cat <: Animal => Box[Cat] <: Box[Animal]
+
+ scala> getName(box)
+ res2: String = Kuzya
+
+
+
+
+
+ +
+ Почему всегда не использовать ковариантность? +
+ + +
+

Contravariance

+
+ B <: A => F[A] <: F[B]
+
+
+
+
+ + trait Printer[-T] {
+   def print(value: T): String
+ } +
+
+
+
+
+ + +
+
+
+
+ + val catPrinter = new Printer[Cat] {
+   override def print(cat: Cat): String =
+     cat.name + " is a cat!"
+ }
+
+ val animalPrinter = new Printer[Animal] {
+   override def print(animal: Animal): String =
+     animal match {
+       case Cat(name) => name + " is a cat!"
+       case Dog(name) => name + " is a dog!"
+     }
+ }
+
+
+
+
+
+ +
+
+
+
+ + Printer[Cat] - может печатать только котов
+
+ Printer[Animal] - может печатать любых животных
+
+ Cat <: Animal =>
  Printer[Animal] <: Printer[Cat] +
+
+
+
+
+ + +
+
+
+
+ + def printCat(
    cat: Cat,
    printer: Printer[Cat]
): String =
  printer.print(cat)
+
+ scala> printCat(
  Cat("Garfield"),
  animalPrinter
)
+ res3: String = Garfield is a cat! +
+
+
+
+
+ + +
+

Position variance

+
+
+
+
+ + trait Printer[+T] {
+   def print(value: T): String
+ }
+
+
+
+
+
+ Что произойдет? +
+ +
+
+
+
+ + trait Printer[+T] {
+   def print(value: T): String
+ }
+
+ <console>:12: error: covariant type T occurs in contravariant position in type T of value value +
+
+
+
+
+ +
+
+
+
+ + case class Box[-T](value: T)
+
+ <console>:11: error: contravariant type T occurs in covariant position in type => T of value value +
+
+
+
+
+ +
+
+
+
+ + trait Function[?A, ?B] {
+   def apply(argument: A): B
+ }
+
+ val name = new Function[Animal, String] {
+   override def apply(
      argument: Animal
  ): String =
    argument.name
+ }
+
+ scala> name(Cat("Kuzya"))
+ res15: String = Kuzya +
+
+
+
+
+ +
+
+
+
+ + trait Function[-A, +B] {
+   def apply(argument: A): B
+ } +
+
+
+
+
+ +
+
+
+
+ + trait GetSet[?T] {
+   def get: T
+
+   def set(value: T): Unit
+ } +
+
+
+
+
+ +
+
+
+
+ + trait GetSet[T] {
+   def get: T
+
+   def set(value: T): Unit
+ } +
+
+
+
+
+ +
+
+
+
+ + trait Function[-A, +B] {
+   def apply(argument: A): B
+
+   def andThen[C](
      f: Function[B, C]
  ): Function[A, C] = { argument: A =>
+     f.apply(apply(argument))
+   }
+ }
+
+ scala> val f = name.andThen(_.length)
+ scala> f(Cat("Garfield"))
+ res0: Int = 8 +
+
+
+
+
+ +
+

Kind

+
+ + +
+
+
+
+ + scala> def incorrect(box: Box) = box
+
+ <console>:13: error: class Box takes type parameters +
+
+
+
+
+ Box не является типом! +
+ +
+

Конкретный тип: *

+
+
+
+
+ +
  • Int
  • +
  • String
  • +
  • Box[Int]
  • +
  • Box[Box[String]]
  • +
  • List[Double]
  • +
  • Box2[Double, String]
  • +
  • Map[String, Int]
  • +
    +
    +
    +
    +
    + +
    +

    Конструктор типа с одним параметром: * -> *

    +
    +
    +
    +
    + +
  • Box
  • +
  • List
  • +
  • Option
  • +
  • Box2[Int, *]
  • +
  • Map[*, Double]
  • +
    +
    +
    +
    +
    + +
    +

    конструктор типа с двумя параметрами: * -> * -> *

    +
    +
    +
    +
    + +
  • Box2
  • +
  • Map
  • +
  • Either
  • +
    +
    +
    +
    +
    + + +
    +

    (* -> *) -> *

    +
    + ??? +
    + + +
    +

    Higher Kinds

    +
    + (* -> *) -> * (конструктор типа, который принимает в качестве аргумента другой конструктор типа)
    +
    + Флаг компилятора:  -language:higherKinds
    +
    +
    +
    +
    + + case class HigherKindedBox[T[_]](
        value: T[Int]
    )
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + scala> HigherKindedBox(Box(5))
    + res10: HigherKindedBox[Box] =
      HigherKindedBox(Box(5))
    +
    + scala> HigherKindedBox(List(5, 6, 7))
    + res11: HigherKindedBox[List] =
      HigherKindedBox(List(5, 6, 7))
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + scala> HigherKindedBox(5)
    +
    + <console>:14: error: ...
    + argument expression's type is not compatible with formal parameter type;
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + scala> HigherKindedBox(Box2("hello", 6))
    +
    +
    +
    +
    +
    + ??? +
    + +
    +
    +
    +
    + + scala> HigherKindedBox(Box2("hello", 6))
    +
    + <console>:16: error: ...
    + argument expression's type is not compatible with formal parameter type;
    +
    + T      = Box2[String, *]
    + T[Int] = Box2[String, Int] +
    +
    +
    +
    +
    + ??? +
    + + +
    +

    Частичная унификация

    + Флаг компилятора:  -Ypartial-unification
    +
    + https://github.com/typelevel/kind-projector
    +
    +
    +
    +
    + + box: HigherKindedBox[Box2[String, *]]=
      HigherKindedBox(Box2("hello", 1)) +
    +
    +
    +
    +
    +
    +
    + + + + + + + +