Я не могу понять, почему и как класс дела Q
преобразуется в функцию (h) типа Out[B] => A
.
Это не так. На самом деле, case class Q
не имеет к этому никакого отношения! Это все о object Q
, который является сопутствующим модулем для case class Q
.
Каждый класс дел имеет автоматически сгенерированный сопутствующий модуль , который содержит (среди прочего) метод apply
, сигнатура которого соответствует первичному конструктору класса сопутствующего, и который создает экземпляр класса сопутствующего.
Т.е. когдавы пишете
case class Foo(bar: Baz)(quux: Corge)
Вы получаете не только автоматически определяемые удобные методы класса дел, такие как средства доступа для всех элементов , toString
, hashCode
, copy
и equals
, но вы также получаете автоматически определенный сопутствующий модуль, который служит как экстрактором для сопоставления с образцом, так и фабрикой для построения объекта:
object Foo {
def apply(bar: Baz)(quux: Corge) = new Foo(bar)(quux)
def unapply(that: Foo): Option[Baz] = ???
}
В Scala apply
- это метод, который позволяет вамдля создания «функциональных» объектов: если foo
является объектом (а не методом), то foo(bar, baz)
переводится в foo.apply(bar, baz)
.
Последний фрагментзагадка η-expansion , который поднимает метод (который не является объектом) в функцию (которая является объектом и, таким образом, может быть переданав качестве аргумента, хранится в переменной и т. д.) Существует две формы η-расширения: явное η-расширение с использованием оператора _
:
val printFunction = println _
И неявное η-расширение: в случаях, когда Scala на 100% знает, что вы имеете в виду функцию, но вы даете ей имя метода, Scala выполнит η-расширение для вас:
Seq(1, 2, 3) foreach println
Ивы уже знаете о карри.
Итак, если мы соберем все вместе:
Q(true)_
Во-первых, мы знаем, что Q
здесь не может быть классомQ
. Откуда мы это знаем? Поскольку Q
здесь используется как значение , но классы являются типами , и, как и большинство языков программирования, Scala имеет строгое разделение между типами и значениями. Следовательно, Q
должно быть значением. В частности, поскольку мы знаем, что класс Q
является классом case, объект Q
является сопутствующим модулем для класса Q
.
Во-вторых, мы знаем, что для значения Q
Q(true)
является синтаксическим сахаром для
Q.apply(true)
В-третьих, мы знаем, что для классов case у сопутствующего модуля есть автоматически сгенерированный метод apply
, который соответствует первичному конструктору, поэтому мы знаем, что Q.apply
имеет два списка параметров.
Итак, наконец, у нас есть
Q.apply(true) _
, который передает первый список аргументов в Q.apply
и затем поднимает Q.apply
в функцию, которая принимает второйсписок аргументов.
Обратите внимание, что классы дел с несколькими списками параметров необычны, поскольку только параметры в первом списке параметров считаются элементами класса дел, и только элементы получают выгоду от "case class magic ", т.е. только элементы получают средства доступа, реализованные автоматически, только элементы используются в сигнатуре метода copy
, только элементы используются в автоматесгенерированные equals
, hashCode
, toString()
методы и т. д.