3author: Ilya Kostyuchenko
6# Функциональное программирование
10Функциональное программирование это не про использование map filter
11reduce. Функциональное программирование это вообще ортогонально к ооп.
12ИМХО проблема с ООП -- наследование (которое тоже ортогонально к ООП)
13-- нет разграничения между абстракцией и реализацией. и вообще
14обзывание это функциональным программированием -- не совсем правильно.
15то, что мы сейчас будем пытаться сделать -- сделать код более
16переиспользуемым. сделать наши данные максимально тупыми =>
17переиспользуемыми. пытаться комбинировать слишком умные куски -- не
18получится -- слишком сложно. мы хотим сделать простые куски, которые
19просто комбинировать и еще чтобы компилятор проверял эти наши комбинации
25<section data-background="images/synthwave.jpeg">
26<h1>Программировать можно без боли</h1>
31Функциональное программирование -- метапрограммирование над эффектами.
39 Coffee buyCoffee(CreditCard cc, Payments p) {
40 Coffee cup = new Coffee();
41 p.charge(cc, cup.price);
49{width=450}
55 Pair<Coffee, Charge> buyCoffee(CreditCard cc) {
56 Coffee cup = new Coffee();
57 return new Pair(cup, cup.price);
67 {width=450}
70 {width=450 .fragment}
78Функциональное программирование:
80> функции -- это тоже данные.
82`Int -> Bool` (функция) -- ровно такие же данные как и `Int`. Их также
83можно складывать в структуры, передавать как аргументы и т. п.
104 <tr class="fragment">
116 <tr class="fragment">
146 <td class="fragment">
151 <td class="fragment">
157 <tr class="fragment">
164 ```{.fragment .haskell}
168 ```{.fragment .haskell}
199 <td class="fragment">
205 <td class="fragment">
225``` {.fragment .haskell}
229``` {.fragment .haskell}
233``` {.fragment .haskell}
250```{ .haskell .fragment }
258<!-- ### моноид {#моноид .fragment} -->
261моноид -- у тебя есть две штуки одного типа и ты можешь получить еще
262одну штуку такого же типа. Это очень простой способ рождения сложности
263из простоты причем тип h выводится из определения. Нам его указывать не
264надо. если у нас изначальные маленькие кусочки хорошо описаны, то
265порожденные большие кусочки будут автоматически хорошо описанными.
272[Бинарная асоциативная операция]{.fragment}
274```{.fragment .haskell}
275(.) :: (a -> a) -> (a -> a) -> (a -> a)
278[Нейтральный элемент]{.fragment}
280```{.fragment .haskell}
291### Чисто функциональный
293Все что функция может сделать -- посмотреть на свои аргументы это
297Нельзя просто произвольно распечатать строку в консоль, нельзя просто
298произвольно отправить запрос на бд, нельзя просто произвольно изменить
299глобальную переменную.
304### Referential transparency
306##### (Любую константу можно заменить на само значение)
311System.out.println(y + " might be the same as " + y);
316System.out.println(x++ + " might be the same as " + x++);
320Сильно упрощает рефакторинг и понимание кода.
329Можно не думать о порядке выражений.
333### Нет переменных, циклов и условных переходов
335[Зато есть константы и нормальная рекурсия (А переходов вообще нет)]{.fragment}
341```{.haskell .fragment}
348`xs'` не нужен чтобы распечатать `xs`, поэтому сортироваться ничто
349не будет. (Зачем делать то, что можно не делать)
354Если хотите быстро и просто потыкаться, тут есть интерктивная штука:
356[haskell.org](https://www.haskell.org)
373Все функции и константы всегда обозначаются словами с маленькой буквы
376(Константы это просто функции с нулем аргументов.)
379Мы буквально говорим что функция на таком-то аргументе равна чему-то.
387fixBuz :: Int -> String
391fixBuz _ = "Some other number"
394Так матчить можно произвольные структуры произвольного уровня
397`_` -- специальное название константы, которое говорит
398что вам все равно что в ней лежит.
401функции объявляются в несколькро строк. Первая -- обьявленик типа
402функции, а последующие -- реализация. В общем случае тип можно не
403указывать, но указывать тип у высказыванй на самом верхнем уровне (не
404вложенные) считается хорошим тоном и улучшает выведение типов и ошибки
405компиляции. У функции может быть несколько реализаций: какая из них
406вызовется зависит от значений передаваемых аргументов. матчинг
407произхводится сверху вниз. если в аргемантах написано слово с маленькой
408буквы, то значение аргумента биндится на эту константу.
419##### (обычные поля структур)
422После констрктора можно укзать существующие типы, которые в нем
427data PersonType = Person String Int
430```{.haskell .fragment}
432vasya = Person "Vasya" 8
433-- тип можно не укзывать
434petya = Person "Petya" 5
437```{.haskell .fragment}
438getName :: PersonType -> String
439getName (Person name _) = name
442```{.haskell .fragment}
443greetPerson :: PersonType -> String
444greetPerson p = "Hello, " ++ getName p
447```{.haskell .fragment}
455vasya = Person "Vasya" 8
456petya = Person "Petya" 5
459```{ .haskell .fragment }
460matchVasya :: PersonType -> Bool
461matchVasya (Person "Vasya" _) = True
465```{ .haskell .fragment }
483`Foo` -- тип структуры. `Bar` -- конструктор структуры.
485Тут у `Foo` всего одно значение `Bar`.
489## Еще немного функций
491```{ .haskell .fragment }
492greetPerson :: PersonType -> String
493greetPerson p = "Hello, " ++ getName p
496```{.fragment .haskell }
497greetPerson :: PersonType -> String
498greetPerson p = "Hello, " ++ name
503```{.fragment .haskell }
504greetPerson :: PersonType -> String
505greetPerson p = "Hello, " ++ name
507 getName' (Person name _) = name
514data PersonType = Person String Int
516getName :: PersonType -> String
517getName (Person name _) = name
520```{.fragment .haskell }
521greetName :: String -> String
522greetName name = "Hello, " ++ name
525```{.fragment .haskell }
526greetPerson :: PersonType -> String
527greetPerson p = greetName (getName p)
530```{.fragment .haskell }
531greetPerson :: PersonType -> String
532greetPerson = greetName . getName
535```{.fragment .haskell }
540```{.fragment .haskell }
541(greetName . getName) petya
550Функции нескольких аргументов -- странный синтаксис -- потом расскажу. А пока используем тюпли!
552В большинстве других языков этого нет и используются всякие странные кодирования типо нулевые указатели, и никто не просит тебя их проверять, что очень плохо.
555```{ .fragment .haskell }
556data Bool = False | True
559```{ .fragment .haskell }
565```{ .fragment .haskell }
566ifThenElse :: (Bool, a, a) -> a
569```{ .fragment .haskell }
570ifThenElse (True, a, _) = a
571ifThenElse (False, _, b) = b
574```{ .fragment .haskell }
575ifThenElse (True, "Hello", "World")
579```{ .fragment .haskell }
580ifThenElse (False, "Hello", "World")
587data CircleType = Circle Double Double Double
588data RectangleType = Rectangle Double Double Double Double
591```{ .fragment .haskell }
593 CircleShape CircleType | RectangleShape RectangleType
596```{ .fragment .haskell }
597surface :: Shape -> Double
600```{ .fragment .haskell }
601surface (CircleShape (Circle _ _ r)) =
605```{ .fragment .haskell }
606surface (RectangleShape (Rectangle x1 y1 x2 y2)) =
607 (abs (x2 - x1)) * (abs (y2 - y1))
610```{ .fragment .haskell }
611shape = CircleShape (Circle 0 0 2)
616```{ .fragment .haskell }
617otherShape = RectangleShape (Rectangle 1 2 3 4)
624# И еще немного функций
630```{ .haskell .fragment }
634```{ .haskell .fragment }
638```{ .haskell .fragment }
642[λ -- `\` (λ печатать тяжело)]{.fragment}
644```{ .haskell .fragment }
645foo :: (Int -> Int) -> Int
648```{ .haskell .fragment }
652```{ .haskell .fragment }
658### Давайте придумаем синтаксис для функции нескольких аргументов!
668```{ .haskell .fragment }
673[Применение функции -- лево-ассоциативно]{.fragment}
675```{ .haskell .fragment }
679```{ .haskell .fragment }
684```{ .haskell .fragment }
688```{ .haskell .fragment }
689add :: Int -> (Int -> Int)
695add :: Int -> (Int -> Int)
698[Тип `->` -- право-ассоциативный]{.fragment}
700```{ .haskell .fragment }
701add :: Int -> Int -> Int
704```{ .haskell .fragment }
708```{ .haskell .fragment }
714Любая функция берет строго один аргумент.
716Функция нескольких аргументов все равно берет строго одтн аргумент и
717возвращает функцию, которая берет следующий.
719*(И из-за того, что применение функции лево-ассоциативно, вызов таких не
720трубует особого синтаксиса.)*
726```{ .haskell .fragment }
727add :: Int -> Int -> Int
731```{ .haskell .fragment }
735```{ .haskell .fragment }
736add :: Int -> (Int -> Int)
739```{ .haskell .fragment }
743```{ .haskell .fragment }
752[Оператор (например `+`) -- функция, название которой не содержит буквы и цифры.]{.fragment}
754```{ .haskell .fragment }
758```{ .haskell .fragment }
767[Оператор можно превратить в функцию, окружив его скобками.]{.fragment}
769```{ .haskell .fragment }
774```{ .haskell .fragment }
779```{ .haskell .fragment }
780add :: Int -> Int -> Int
784```{ .haskell .fragment }
788```{ .haskell .fragment }
796[Функцию можно превратить в оператор, окружив ее обратными кавычками.]{.fragment}
799```{ .haskell .fragment }
800add :: Int -> Int -> Int
804```{ .haskell .fragment }
809```{ .haskell .fragment }
820### Односвязный список
822```{ .haskell .fragment }
823data IntList = Nil | Cons Int IntList
826```{ .haskell .fragment }
827Cons :: Int -> IntList -> IntList
831```{ .haskell .fragment }
833nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
836```{ .haskell .fragment }
840```{ .haskell .fragment }
841sum (Cons x xs) = x + sum xs
844```{ .haskell .fragment }
846sum (Cons x xs) = x + sum xs
849```{ .haskell .fragment }
857take :: Int -> IntList -> IntList
860```{ .haskell .fragment }
863take n (Cons x xs) = Cons x (take (n - 1) xs)
866```{ .haskell .fragment }
868nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
871```{ .haskell .fragment }
873-- Cons 1 (Cons 2 Nil)
875-- Cons 1 (Cons 2 (Cons 3 Nil))
883repeat :: Int -> IntList
886```{ .haskell .fragment }
887repeat n = Cons n (repeat n)
890```{ .haskell .fragment }
892-- Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (...
895```{ .haskell .fragment }
897-- Cons 8 (Cons 8 (Cons 8 Nil))
900```{ .haskell .fragment }
901(sum . take 3 . repeat) 8
910 Наша самодеятельность
913 В стандартной библиотеке
916 <tr class="fragment">
927 <tr class="fragment">
939 <tr class="fragment">
951 <tr class="fragment">
958 ``` {.fragment .haskell }
962 ``` {.fragment .haskell }
972data IntList = Nil | Cons Int IntList
975```{ .haskell .fragment }
976data [Int] = [] | Int : [Int]
981`repeat`, `sum` и `take` тоже есть в стандартной библиотеке.
987-- [8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, ...
990```{ .haskell .fragment }
992-- [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
995```{ .haskell .fragment }
1000```{ .haskell .fragment }
1005```{ .haskell .fragment }
1017quicksort :: [Int] -> [Int]
1020```{ .haskell .fragment}
1022 quicksort smaller ++ [x] ++ quicksort larger
1024 smaller = filter (< x) xs
1025 larger = filter (>= x) xs
1031filter :: (Bool -> Int) -> [Int] -> [Int]
1034```{ .haskell .fragment}
1037 then x:(filter f xs)
1041```{ .haskell .fragment}
1046 else x:(filter f xs)
1052quicksort :: [Int] -> [Int]
1054 quicksort smaller ++ [x] ++ quicksort larger
1056 smaller = filter (< x) xs
1057 larger = filter (>= x) xs
1062 else x:(filter f xs)
1068quicksort :: [Int] -> [Int]
1071 quicksort smaller ++ [x] ++ quicksort larger
1073 smaller = filter (< x) xs
1074 larger = filter (>= x) xs
1079 else x:(filter f xs)
1082```{ .haskell .fragment}
1083quicksort [2, 1, 3, 4]
1089`filter` тоже есть в стандартной библиотеке.
1093# Еще немного структур
1097## Параметрический полиморфизм
1101```{ .haskell .fragment }
1102data IntList = Cons Int IntList | Nil
1106Мы научиоись делать список интов. А что если мы хотим сделать просто список. Чего-нибудь.
1109```{ .haskell .fragment }
1110data List a = Cons a (List a) | Nil
1113```{ .java .fragment }
1117```{ .haskell .fragment }
1119ints = Cons 1 (Cons 2 (Cons 3 Nil))
1122```{ .haskell .fragment }
1123-- Типы как всегда можно не писать
1124strings :: List String
1125strings = Cons "one" (Cons "two" (Cons "three" Nil))
1131things = Cons "one" (Cons 2 Nil)
1135 • Couldn't match type ‘Int’ with ‘String’
1136 Expected type: List String
1137 Actual type: List Int
113910 | things = Cons "one" (Cons 2 Nil)
1143```{ .haskell .fragment }
1144data List a = Cons a (List a) | Nil
1147[`a` должен всегда быть `a`.]{.fragment}
1152take :: Int -> IntList -> IntList
1155take n (Cons x xs) = Cons x (take (n - 1) xs)
1158```{ .haskell .fragment }
1159take :: Int -> List a -> List a
1162take n (Cons x xs) = Cons x (take (n - 1) xs)
1166Поменялся только тип!
1171# Где и как смотреть "стандартную библиотеку"
11751. [Hackage](https://hackage.haskell.org/package/base)
1177 (Там вам нужен только пакет `base`. Ссылка ведет прямо на него.)
1178 Еще если там нажать `s`, то будет поиск.
11802. [Hoogle](https://hoogle.haskell.org)
1182 Это поиск по типам. Например: `Int -> [Int] -> [Int]` (Тут
1183 вам опять же нужен только пакет `base`. Нужно чтобы справа было
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list