Статьи

Скала: Отсутствует мост OO и FP

Scala сочетает функциональное и объектно-ориентированное программирование многими приятными способами. Вы можете использовать как FP, так и OO-подобные конструкции, в зависимости от того, что лучше подходит для текущей задачи. Но всегда есть возможности для улучшения! Вот одна вещь, я думаю, отсутствует ( короткая версия внизу).

Сторона 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 list
def connect(person1: Person)(person2: person): Connection = { ... }
// Function, created by partially applying the previous method
val createConnectionWithAdam: Person => Connection =
      connect(new Person('Adam')) _

Оо сторона

В каждом классе есть конструктор. Но что на самом деле конструктор? Вы даете значения для аргументов конструктора и получаете взамен новый экземпляр класса. Так что это действительно просто функция!

1
2
3
class Person(name: String, age: Int) { ... }
// The 'signature' of new is (String, Int) => Person
val 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 instance
class 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
// instances
class 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) => Person
 
class Person2(name: String)(age: Int)
val makeNewJack = new Person2('Jack') _ // type: Int => Person

Ссылка: отсутствует блог OO и FP в Scala от нашего партнера по JCG Адама Варски в