Теории и практики фунционального программирования - gdg...
TRANSCRIPT
Теории и практики функционального
программирования
Акуляков Артем
Кто я?
● 5+ лет dotNet interpraise● 2+ лет python freelance● 2+ I love FP
● Докладчик dotnetconf, dev2dev● Организатор it-сообщества dev2dev
2
К чему это все?
3
К чему это все?
● ФП устарело.
4
ФП устарело, но вот эти ребята еще не в курсе...
5
К чему это все?
● ФП устарело.● ФП применимо только в узком круге задач.
6
Веб-приложение
HttpRequest → HttpResponse
7
Веб-приложение
function (HttpRequest) → HttpResponse
8
К чему это все?
● ФП устарело.● ФП применимо только в узком круге задач.
● Я БОЮСЬ ФУНКЦИОНАЛЬНОГО ПРОГРАММИРОВАНИЯ!
9
Поговорим об ООП
10
ООП против ФП● SOLID● IoC● GRASP● GangOfFour● MVC, MVP, MVVM● CQRS● EventSourcing● DDD● ...
11
ООП против ФП● SOLID● IoC● GRASP● GangOfFour● MVC, MVP, MVVM● CQRS● EventSourcing● DDD● ...
● Functions● Type● Composition
12
ООП против ФП● SOLID● IoC● GRASP● GangOfFour● MVC, MVP, MVVM● CQRS● EventSourcing● DDD● ...
● Functions● Type● Composition
● Monads, Monoids...
13
Основы функционального программирования
14
Функции это тоже значения
15
Функции это тоже значения
let value = 5
let toX10 = fun x -> x * 10
16
Функции это тоже значения
let getCalc f x y = fun z -> (f x) + y + z
let calc = getCalc toX10 1 2
let result = calc value
17
Композиция
18
Композиция : функции
let read (x : string) : int = int(x)
let mult (x : int) : int = x * x
let write (x : int) : string = string(x)
19
Композиция : функции
let readMultWrite1 x = write (mult (read x))
// x : string -> string
let readMultWrite2 = read >> mult >> write
// string -> string
let readMultWrite3 x = x |> read |> mult |> write
// x : string -> string
20
Композиция
a → b » b → ca → c
21
Композиция
a → b » b → c » ... » x → za → z
22
Композиция : функции#операторыкомпозиции
let (>>) f1 f2 p = f2(f1 p)let (<<) f1 f2 p = f1(f2 p)
23
Композиция : функции#конвейерныеоператоры
let (|>) p f = func paramlet (<|) f p = func param
24
Проблема...Операторы композиции и
конвейера определены только для функций с одним аргументом
25
Решение!Все функции это на самом деле функции от одного аргумента
26
Композиция : функции #каррирование
let add x y = x + y // int -> int -> int
let add2 = add 2 // int -> int
let res = add2 2 // val res : int = 4
27
Типы
28
Типы#обзор
type Alias = inttype FunctionAlias = int -> int
29
Типы#обзор
type Record = { field1 : int; field2 : string }
30
Типы#обзор
type DiscriminatedUnion = | Variant1 of int | Variant2 of string
type Enum = | One = 1 | Two = 2 | Three = 3
31
Типы это не классы
32
Композиция : типы
type NodeName = stringtype UID = int
type HierarchyIdentifier = | Plain of UID | Complex of NodeName*HierarchyIdentifier
33
Композиция: типы
Complex(NodeName * Complex
(NodeName * Complex(Plain)
))
34
ООП и ФП имеют много общих концепций
35
Interfaces & SRP
public interface IExchangeRateProvider { ExchangeRate GetRate(Currency currency); }
public class ExchangeProvider : IExchangeRateProvider { public ExchangeRate GetRate(Currency currency) { // =) } }
36
Interfaces & SRP#типыфункцийкакинтерфейсы
type IExchangeRateProvider = Currency -> ExchangeRate
let exchangeRateProvider(currency:Currency):ExchangeRate = // =)
37
DI
public class BasketCostTranslator : IBasketCostTranslator{ private readonly IExchangeRateProvider _provider;
public BasketCostTranslator(IExchangeRateProvider p) { // =) }
public BasketCost TranslateCost(BasketCost cost) { // =) }}
38
DI#кариррованиекакdi
type ICostTranslator = BasketCost -> BasketCost
let costTranslator exchangeProvider basket = // =)
let translator = costTranslator exchangeRateProvider
39
Шаблонный методpublic class SimpleDeliveryCalculator : IDeliveryCostCalculator{ public DeliveryOffer Calculate(
IEnumerable<PurchaseDimensions> purchases) { // do something var packagesCount = CalculatePackagesCount(purchases); // do something }
protected virtual int CalculatePackagesCount(IEnumerable<PurchaseDimensions> purchases)
{ // calculation }}
40
Шаблонный метод
public class ComplexDeliveryCalculator: SimpleDeliveryCalculator{ protected override int CalculatePackagesCount(
IEnumerable<PurchaseDimensions> purchases) { // "complex" calculation }}
41
Шаблонный метод#композициярулит
let deliveryCalculator packagesCalculator purchases = // do something let packagesCount = packagesCalculator purchases // do something
let simpleCalculatePackages purchases = // do something
let complexCalculatePackages purchases = //do something
let calculator1 = deliveryCalculator simpleCalculatePackageslet calculator2 = deliveryCalculator complexCalculatePackages
42
Декораторpublic class LoggingDecorator : IExchangeRateProvider{ private IExchangeRateProvider _service;
public LoggingDecorator(IExchangeRateProvider service) { ... }
public ExchangeRate GetRate(Currency currency) { // log something var result = _service.GetRate(currency); // log something return result; }}
43
Декоратор#исновакомпозициярулит
let loggingDecorator provider currency = // log something let result = provider currency // log something result
let provider = loggingDecorator exchangeRateProvider
44
Декоратор#pyдекораторыпрекрасны
def logging(func): def wrapper(*args, **kwargs): res = func(*args, **kwargs) print(func.__name__, args, kwargs) return res return wrapper
@loggingdef adder(x, y): return x + y
adder(1, 2)
45
Кое-что интересное из мира ФП
46
Мемоизация
47
Мемоизация#чистыефункции
let adder x = x + 100
adder 10 // val it : int = 110adder 10 // val it : int = 110adder 20 // val it : int = 120
48
Мемоизация#ультракеширование
let memoize (f: 'a -> 'b) = let dict = new Dictionary<'a, 'b>() let memoized (input: 'a) = match dict.TryGetValue(input) with | true, x -> x | false, _ -> let result = f input dict.Add(input, result) result memoized
let memAdder = memoize adder
49
Мемоизация#ультракеширование
let memoize (f: 'a -> 'b) = let dict = new Dictionary<'a, 'b>() let memoized (input: 'a) = match dict.TryGetValue(input) with | true, x -> x | false, _ -> let result = f input dict.Add(input, result) result memoized
let memAdder = memoize adder
50
Мемоизация#ультракеширование
let memoize (f: 'a -> 'b) = let dict = new Dictionary<'a, 'b>() let memoized (input: 'a) = match dict.TryGetValue(input) with | true, x -> x | false, _ -> let result = f input dict.Add(input, result) result memoized
let memAdder = memoize adder
51
Мемоизация#практика
function (HttpRequest) → HttpResponse
52
Мемоизация#практика
function (HttpRequest, DbState) → HttpResponse
53
Мемоизация#практика
https://github.com/Suor/django-cacheops
54
Обработка ошибок
55
Обработка ошибок #идеальныймир
let handleRequest = readRequest >> readEntityFromDB >> modifyEntity >> createTaskFromEntity >> writeTaskToMQ >> createResultMessage >> sendMessage
56
Мир неидеален
57
Обработка ошибок
type Result<'r> = | Success of 'r | Error
// 'r -> Result<'r>
58
Обработка ошибок let handleRequest s = match readRequest s with | Success r -> match readEntityFromDB r with | Success e -> match modifyEntity e with | Success me -> match createTaskFromEntity me with | Success t -> match writeTaskToMQ t with | Success ts -> createResultMessage ts | Error -> Error | Error -> Error | Error -> Error | Error -> Error | Error -> Error
59
А как же композиция?
»
60
Обработка ошибок#неработает
'a → Result<'b>
» 'b → Result<'c>
» 'c → Result<'d>
» …
Типы не совпадают...
61
Обработка ошибок#решение
Нужно преобразование
('a → Result<'c>) → (Result<'a> → Result<'c>)
62
Обработка ошибок#решение
Нужно преобразование
('a → Result<'c>) → (Result<'a> → Result<'c>)
Как?
63
Обработка ошибок let handleRequest s = match readRequest s with | Success r -> match readEntityFromDB r with | Success e -> match modifyEntity e with | Success me -> match createTaskFromEntity me with | Success t -> match writeTaskToMQ t with | Success ts -> createResultMessage ts | Error -> Error | Error -> Error | Error -> Error | Error -> Error | Error -> Error
64
Обработка ошибок#паттернобнаружен
let handleRequest s = match readRequest s with | Success r -> match readEntityFromDB r with | Success e -> match modifyEntity e with | Success me -> match createTaskFromEntity me with | Success t -> match writeTaskToMQ t with | Success ts -> createResultMessage ts | Error -> Error | Error -> Error | Error -> Error | Error -> Error | Error -> Error
65
Обработка ошибок#связываем
let bind f = fun x -> match x with | Success r -> f r | Error -> x
('a → Result<'b>) → (Result<'a> → Result<'b>)
66
Обработка ошибок#композиция
let handleRequest = readRequest >> (bind readEntityFromDB) >> (bind modifyEntity) >> (bind createTaskFromEntity) >> (bind writeTaskToMQ) >> (bind createResultMessage)
67
Мы только что изобрели монаду
68
Сухой итог
● Функциональное программирование это просто● Можно использовать привычные приемы, но
проще● Можно заимствовать из функционального
программирования хитрые приемы● Монады совсем не страшные
69
Полезности
http://www.intuit.ru/studies/courses/471/327/info
https://github.com/fsprojects/FSharpx.Extras
https://github.com/jack-pappas/ExtCore/
http://fsharpforfunandprofit.com/
70
Вопросы?
71