Функциональные принципы
для
ООП-разработчика
Алексей Пирогов @alex_pir

Presenter Notes

Подходы к разработке ПО

  • Императивный
    • Процедурный
    • Объектно-ориентированный
    • Аспектно-ориентированный
  • Декларативный
    • Функциональный
    • Логический

Presenter Notes

Подходы к разработке ПО

Подход, это инструмент!

  • Инструменты помогают решать задачи!
  • Каждый инструмент имеет назначение
  • Универсальные инструменты всегда хуже специализированных

Presenter Notes

При этом

Не стоит хвататься за новый молоток, если нужно вкрутить винт в гайку, но и старой удобной отверткой гвозди забивать не нужно!

Presenter Notes

Программа передач

  1. Чистота
  2. Специфичность типов
  3. Функции в роли данных
  4. Неизменяемость
  5. Декларативность
  6. Ленивость

Presenter Notes

1. Чистота

Presenter Notes

Чистая функция
==
трансформация данных

Presenter Notes

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

  • НЕ взаимодействует с глобальным состоянием
  • НЕ модифицирует входные данные
  • НЕ меняет мир!

Presenter Notes

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

  • предсказуема
  • удобна для тестирования
  • удобна для документирования
  • удобна для повторного использования

Presenter Notes

Инкапсуляция

Presenter Notes

Изоляция

Presenter Notes

Изоляция

1 Response createAccount(
2     UserDbService svc,
3     Request req,
4     Config cfg) {
5   Account acc = constructAccount(...);
6   AccountInsertResult r = svc.insert(acc);
7   return respond(r);
8 }

Presenter Notes

Изоляция

1 Response createAccount(
2     Function<Account,
3              AccountInsertResult> svc,
4     Request req,
5     Config cfg) {
6   Account acc = constructAccount(...);
7   AccountInsertResult r = svc.apply(acc);
8   return respond(r);
9 }

Presenter Notes

2. Специфичность типов

Presenter Notes

Java

1 public Customer(String name, String em)

Presenter Notes

Java

 1 public Customer(FirstName name,
 2                 EmailAdderss em)
 3 ...
 4 public class FirstName {
 5     public final String stringValue;
 6     public FirstName(final String value) {
 7         this.stringValue = value;
 8     }
 9     public String toString() {...}
10     public boolean equals() {...}
11     public int hashCode() {...}
12 }

Presenter Notes

Scala

1 case class FirstName(name: String)
2 
3 val name = FirstName("Moe")

Presenter Notes

Scala

1 type FirstName = String
2 
3 val name: FirstName = "Moe"

Presenter Notes

Выразительность

Presenter Notes

Выразительность

1           Time => Cost
2           Time => Volume
3 (Volume, Cost) => Expediture
4               ....
5           Time => Expediture

Presenter Notes

Надёжность

1 Account => AccountInsertResult

Presenter Notes

Ошибки, это тоже данные!

Presenter Notes

Хватит терпеть NPE!

1 val result: Option[String] = "OK"
2 val error:  Option[String] = None

FSharpOption<T>

Optional<T>

Presenter Notes

Работа над ошибками

1 Account => Option[AccountInserted]

или даже

1 Account => Either[AccountInsertFailure,
2                   AccountInserted]

Presenter Notes

3. Функции в роли данных

Presenter Notes

Функции в роли данных

1 class Inflation implements FunctionOverTime
2 {
3     public float valueAt(int t) {
4         return // некое вычисление
5     }
6 }

Presenter Notes

Функции в роли данных

 1 class Variable implements FunctionOverTime
 2 {
 3     private final Function<Int, Double> vals;
 4 
 5     public float valueAt(int t) {
 6         return vals.apply(t);
 7     }
 8 }
 9 Variable inflation = new Variable(
10                      "Cost Inflation",
11                      t -> Math.Pow(1.15, t));

Presenter Notes

Паттерны
Command и Strategy

Presenter Notes

Функции композируемы!

Presenter Notes

Композиция

1 withTransaction(...) {
2     Response r = createAccount(
3         (acc) -> userDB.insert(acc),
4         req,
5         cfg);
6 }

Presenter Notes

Композиция

1 Response r = createAccount(
2     (acc) ->
3     withTransaction(
4         userDB.insert(acc)),
5     req,
6     cfg);

Presenter Notes

Композиция

1 Response r = createAccount(
2     (acc) ->
3     retrying(
4     withTransaction(
5         userDB.insert(acc))),
6     req,
7     cfg);

Presenter Notes

4. Неизменяемость

Presenter Notes

Неизменяемость

Scala

1 val x = 10;
2 var y = 30; // mutable!
3 y = 42;

F#

1 let x = 10;
2 let mutable y = 30;
3 x = 21;  // false
4 y <- 42; // деструктивное обновление!

Presenter Notes

Concurrency

Легко!

1 public class Address {
2     public final String city; // FINAL
3 
4     public Address(String city,...) {
5         this.city = city;
6     }
7 }

Presenter Notes

Защитное копирование

1 private final ImmutableList<Phone> phones;
2 
3 public Customer(Iterable<Phone> phones) {
4     this.phones = Immutablelist.copyOf(phones);
5 }

Presenter Notes

Copy-on-Write

 1 public Customer addPhone(Phone newPhone) {
 2     Iterable<Phone> morePhones =
 3         // boilerplate
 4         Immutablelist.builder()
 5             .addAll(phones)
 6             .add(newPhone)
 7             .build();
 8         // end of boilerplate
 9     return new Customer(morePhones);
10 }

Presenter Notes

Copy-on-Write

Haskell

1 addPhone :: Customer -> Phone -> Customer
2 addPhone c phone =
3          c { phones = phone:(phones c) }

F#

1 member this.addPhone(newPhone: Phone) {
2     new Customer(newPhone :: phones)
3 }

Presenter Notes

Persistent Data Structures

Presenter Notes

5. Декларативность

Presenter Notes

Не как, но что

Presenter Notes

SQL

1 SELECT name, email
2 FROM users
3 WHERE user_id = 42

Presenter Notes

Java

 1 public List<String> findReportBugs(
 2                     List<String> lines) {
 3 
 4     List<String> output = new LinkedList();
 5 
 6     for (String s : lines) {
 7         if (s.startswith("BUG")) {
 8             output.add(s);
 9         }
10     }
11     return output;
12 }

Presenter Notes

Привычно != читаемо

Presenter Notes

Читаемо

Java

1 lines.stream()
2     .filter(s -> s.startswith("BUG"))
3     .collect(Collectors.toList)

C#

1 lines.Where(s => s.StartsWith("BUG")).ToList

Presenter Notes

6. Ленивость

Presenter Notes

Ленивые вычисления

Вычисляем что-либо, тогда и только тогда,
когда результат вычисления потребуется

Presenter Notes

Java

 1 int bugCount = 0;
 2 String nextLine = file.readLine();
 3 while (bugCount < 40) {
 4     if (nextLine.startsWith("BUG")) {
 5         String[] words = nextLine.split(" ");
 6         report("Saw "+words[0]+" on "+words[1]);
 7         bugCount++;
 8     }
 9     waitUntilFileHasMoreData(file);
10     nextLine = file.readLine();
11 }

Presenter Notes

Разделение обязанностей

Отделяем то, что нужно сделать,
от момента, когда нужно остановиться.

Presenter Notes

Java 6

1 for (String s:
2   FluentIterable.of(file)
3     .filter(STARTS_WITH_BUG)
4     .transform(TRANSFORM_BUG)
5     .limit(40)
6     .asImmutableList()) {
7       report(s);
8 }

Presenter Notes

Функциональные принципы

  1. Чистота
  2. Специфичность типов
  3. Функции в роли данных
  4. Неизменяемость
  5. Декларативность
  6. Ленивость

Presenter Notes

Спасибо за внимание

Presenter Notes