Functional Programming becomes very popular nowadays. What is it? Is it a hype or panacea? Should you deal with it as a PHP programmer? Let's find out!
2. Зачем?
Может это просто хайп?
Зачем это PHP девелоперу?
Это так сложно и непонятно… может не надо?
Будут ли мои программы лучше работать?
Сэкономит ли это наше время/деньги?
Разве это спрашивают на собеседовании?
8. Почему мы не можем уснуть по ночам?
Как писать код без ошибок?
Как писать код правильно?
Какой код правильный?
Что такое код?
В чем смысл жизни программирования?
Почему эти вопросы лезут мне в голову?
10. Дремучие времена
Готфрид Вильгельм Лейбниц
1646 - 1716
● Великий учёный
● Придумал интеграл
● Занимался физикой
● Делал механический калькулятор
● И вообще красавчик
Да, я такой
11. Лейбниц -
котик
Считать лениво - нужно
сделать механический
калькулятор
Теоремы доказывать
лениво, нужно сделать
механический
доказыватель теорем
Нужно придумать язык для
описания теорем
механическому устройству
12. Прошло немного времени
Давид Гильберт
1862 - 1943
● Светило математической науки
● Исследовал теорию чисел
● Озвучил Entscheidungsproblem (проблема
разрешимости)
Entscheidungsproblem:
Найти алгоритм, на вход которому дается
описание формального языка и утверждение
на этом языке, а на выходе мы получаем
“Истинно” или “Ложно” это утверждение
14. Прямая связь с программированием
Найти алгоритм, который даст нам ответы на вопросы:
Будет ли наша программа работать?
Есть ли в программе “баги”?
Может ли наша программа “зависнуть”?
Можно ли написать программу, которая может проверять
другие программы на корректность?
17. Машина Тьюринга
● Бесконечная лента с ячейками
● Конечный алфавит символов
● Головка записи-чтения (мозг)
○ конечное число состояний
○ конечное число переходов: в
зависимости от состояния и
символа в ячейке может его
поменять, поменять свое
состояние и двинуться
влево/вправо по ленте.
20. Вычисления в лямбда-исчислении (2)
Бесконечное вычисление:
D = 𝛌x.xx
DD = (𝛌x.xx)(𝛌x.xx) = xx [x:=𝛌x.xx] => (𝛌x.xx)(𝛌x.xx) => ...
F = 𝛌x.y
Вычисление по имени:
F(DD) = (𝛌x.y)(DD) = y[x:=DD] => y
Вычисление по значению:
F(DD) = F((𝛌x.xx)(𝛌x.xx)) => F((𝛌x.xx)(𝛌x.xx)) => ...
21. Почему “функциональное”?
𝛌-абстракция ведет себя как математическая функция
F = 𝛌x.(x+1) ⇔ F(x) = x+1
● Результат функции зависит только от аргументов
● У функции нет побочных эффектов
● У функции нет внутреннего состояния
К тому же:
● Аргументом функции может быть другая функция
24. Лямбда и Entscheidungsproblem
𝛌-исчисление позволяет описывать алгоритмы.
Позволяет ли 𝛌-исчисление описать алгоритм, который
будет проверять корректность других алгоритмов?
25. Лямбда и Entscheidungsproblem (2)
Отнюдь…
В 1936 году Алонзо Черч (а за ним и Тьюринг) доказал,
что такого алгоритма не может существовать.
Доказательство на пальцах: парадокс брадобрея.
26. Что же делать?
Существуют ли идеальные программы, если не
существует общего алгоритма, по которому мы можем их
проверять на корректность?
А может мы просто ограничимся теми программами,
которые мы можем проверять?
27. Типизированное лямбда-исчисление
M:𝛕
1. Если переменная x имеет тип 𝛕, то выражение “x”
тоже будет иметь тип 𝛕
2. Если x имеет тип 𝛕, а выражение М имеет тип 𝛔, то
выражение 𝛌x.M будет иметь тип “𝛕→𝛔”
3. Если М имеет тип 𝛕→𝛔, а N имеет тип 𝛕, то M N будет
иметь тип 𝛔
28. Типизированное 𝛌-исчисление: что оно нам дает?
Типы гарантируют завершаемость вычислений
Типы заставляют нас писать только “хорошие”
программы
29. Но как жить со всеми этими типами?
Между типами могут быть какие-то отношения
Между значениями и типами могут быть отношения
(например: упорядоченные массивы)
=> Существует довольно нетривиальная теория типов и
куча вопросов по этому поводу
32. Промежуточные выводы
Функциональный подход программирования происходит
от одного из формальных определений алгоритма: 𝛌-
исчисления
Функциональный подход ближе к математике, чем к
суровой действительности
Изучение функционального программирования поможет
нам приблизиться к пониманию “что такое хороший
код”
42. Функциональные структуры данных: список
<?php
abstract class AList {
public abstract function head();
public abstract function tail(): AList;
public abstract function isEmpty();
use RichList;
}
class EmptyList extends AList {
public function head() { throw new Exception(); }
public function tail(): AList { throw new Exception(); }
public function isEmpty() { return true; }
}
43. Функциональные структуры данных: список (2)
<?php
class SomeList extends AList {
private $head;
private $tail;
public function __construct($head, AList $tail) {
$this->head = $head;
$this->tail = $tail;
}
public function head() { return $this->head; }
public function tail():AList { return $this->tail; }
public function isEmpty() { return false; }
}
$myList = new SomeList(1, new SomeList(2, new SomeList(3,
new EmptyList()));
44. Функциональные структуры данных: список (3)
<?php
trait RichList {
function doForEach($callback) {
if (!$this->isEmpty()) {
$callback($this->head());
$this->tail()->doForEach($callback);
}
}
function map($callback) {
return $this->isEmpty()
? new EmptyList()
: new SomeList($callback($this->head()),
$this->tail()->map($callback));
}
}
48. Монады: что это?
X + Y + Z
getFromDB() + pullFromQeue() + readFromStdin()
Service not
available :(
Empty :( Invalid :(
M(X) + M(Y) + M(Z)
49. Монады: Maybe
<?php
class Person {
public $name;
public $child;
}
class PersonService {
public static function getPerson(): Person {
// ... load from database
}
}
$getChild = function(Person $p) { return $p->child; };
echo $getChild($getChild(PersonService::getPerson()))->name;
50. Монады: Maybe (2)
<?php
class MaybePerson {
public function flatMap($f) {
return $this instanceof SomePerson
? $f($this->getPerson())
: new None();
}
}
class SomePerson extends MaybePerson {
private $person;
public function __construct(Person $person) {
$this->person = $person;
}
public function getPerson() { return $this->person; }
}
class None extends MaybePerson {}
51. Монады: Maybe (3)
<?php
class PersonService {
public static function getPerson(): MaybePerson { … }
}
$maybeSomeone = PersonService::getPerson()
->flatMap($getChild)
->flatMap($getChild);
echo $maybeSomeone instanceof SomePerson
? $maybeSomeone->getPerson()->name
: "No person";
55. Преимущества функционального подхода
1. Программы легко распараллеливаются и тестируются
2. Есть мощный механизм проверки качества программы
- типизация
3. Отсутствие джуниор-девелоперов на проекте :)
56. Недостатки функционального подхода в PHP
нет функциональных типов
не нужно распараллеливание
нет ленивых вычислений аргументов (вычисление “по
имени”)
нет “хвостовой рекурсии” (что за зверь?)
нет pattern-matching, for-comprehension и других крутых
вещей
много джуниоров
“А зачем?” (в том числе на уровне разработчиков языка)
57. Но не расстраивайтесь!
Живые программы не очень то
функциональны!
ФП нужны специфические “воркараунды” для связывания
мира и математики.
Например, специализированные языки
программирования.
58. В образовательных целях... ;)
1. Scala - язык с человеческим лицом
2. Erlang, Elixir
3. Haskell - функциональный язык для фанатов
4. Idris, Agda, Coq - для математиков
5. Lisp, Closure - :))))))))
59. “В PHP все не так плохо с ФП”
1. https://github.com/lstrojny/functional-php
2. https://github.com/reactphp
3. https://github.com/ReactiveX/RxPHP
4. …
5. ...
60. Вопросы?
1. Зачем?
2. Я ничего не понял, что делать?
3. Можно ли это использовать в production?
4. А скоро обед?
5. ...