Сторона FP
В Scala легко преобразовать метод в функцию. Например, если у вас есть метод:
|
1
|
def say(to: Person, what: String): String = { ... } |
мы можем получить соответствующую функцию, используя символ подчеркивания:
|
1
|
val sayFun: (Person, String) => String = say _ |
Кроме того, Scala поддерживает несколько списков параметров, которые иногда также называют каррированием и которые облегчают частичное применение:
|
1
2
3
4
5
|
// Method with multiple parameter listdef connect(person1: Person)(person2: person): Connection = { ... }// Function, created by partially applying the previous methodval createConnectionWithAdam: Person => Connection = connect(new Person('Adam')) _ |
Оо сторона
В каждом классе есть конструктор. Но что на самом деле конструктор? Вы даете значения для аргументов конструктора и получаете взамен новый экземпляр класса. Так что это действительно просто функция!
|
1
2
3
|
class Person(name: String, age: Int) { ... }// The 'signature' of new is (String, Int) => Personval somebody = new Person('John', 34) |
Ссылка на сайт?
Однако в Scala комбинация OO и FP терпит неудачу: вы не можете использовать две функции методов (преобразование в функцию, каррирование), упомянутые выше, с конструкторами. Оба из них не будут работать :
|
1
2
3
4
|
val makePerson: (String, Int) => Person = new Person _class Person2(name: String)(age: Int) { ... }val makeNewJack: Int => Person = new Person2('Jack') _ |
Вы можете обойти это, используя сопутствующие объекты и apply (или любой другой фабричный метод, применить просто имеет более хорошие обозначения впоследствии):
|
1
2
3
4
5
|
object Person2 { def apply(name: String)(age: Int) = new Person2(name, age)}val makeNewJack: Int => Person = Person2('Jack') _ |
Но для этого нужно повторить подпись конструктора в объекте-компаньоне, и никто не любит дублирование кода, верно? 😉
Использование регистра
Где это может быть полезно? Например в классическом заводском примере. Представьте, что у вас есть класс, который зависит от некоторых служб, а также от некоторых данных, доступных во время выполнения. Конечно, мы используем IoC, поэтому экземпляры других сервисов предоставляются нашему классу:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// This service depends on a concrete Person instanceclass Service3(service1: Service1, service2: Service2)(person: Person) {...}/ Note that the multiple parameter notation above als provides a nice// separation of the parameters that should be 'injected' - services,// and the data that can be variable.// This service depends on Service1, and wants to create it having Person// instancesclass Service4(makeService3: Person => Service3) { // Usage: for (person <- persons) makeService3(person).doSomething()}class Main { // Bootstrap: (or - no-framework DI container 😉 ) val service1 = new Service1 val service2 = new Service2 // That's the part that is illegal in Scala val makeService3 = new Service3(service1, service2) _ val service4 = new Service4(makeService1)...// Today we'd have to write: val makeService3 = (person: Person) => new Service3(//service1, service2, person)} |
Это также связано с моим постом о DI и OO , а также о том, как современные структуры DI затрудняют определение сервисов, которые зависят от данных и сервисов, для которых мы хотели бы иметь несколько копий.
Примечание
При просмотре конструкторов как методов / функций (какими они на самом деле являются;)), я предполагаю Ruby-подобную нотацию:
|
1
|
Person.new('John', 34) |
было бы лучше, и добавление поддержки _ и нескольких списков параметров было бы очевидно.
Итог для поклонников TL; DR
Почему бы не рассматривать конструкторы классов как любой другой метод / функцию? Сделайте это законным:
|
1
2
3
4
5
|
class Person(name: String, age: Int)val makePerson = new Person _ // type: (String, Int) => Personclass Person2(name: String)(age: Int)val makeNewJack = new Person2('Jack') _ // type: Int => Person |
Ссылка: отсутствует блог OO и FP в Scala от нашего партнера по JCG Адама Варски в