Теории и практики
функционального
программирования
Акуляков Артем
Кто я?
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 → c
a → c
21
Композиция
a → b » b → c » ... » x → z
a → z
22
Композиция : функции
#операторыкомпозиции
let (>>) f1 f2 p = f2(f1 p)
let (<<) f1 f2 p = f1(f2 p)
23
Композиция : функции
#конвейерныеоператоры
let (|>) p f = func param
let (<|) 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 = int
type 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 = string
type 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 simpleCalculatePackages
let 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
@logging
def adder(x, y):
return x + y
adder(1, 2)
45
Кое-что интересное из мира ФП
46
Мемоизация
47
Мемоизация
#чистыефункции
let adder x =
x + 100
adder 10 // val it : int = 110
adder 10 // val it : int = 110
adder 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
Обработка ошибок
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 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 bind f =
fun x ->
match x with
| Success r -> f r
| Error -> x
('a → Result<'b>) → (Result<'a> → Result<'b>)
65
Обработка ошибок
#композиция
let handleRequest =
readRequest
>> (bind readEntityFromDB)
>> (bind modifyEntity)
>> (bind createTaskFromEntity)
>> (bind writeTaskToMQ)
>> (bind createResultMessage)
66
Мы только что изобрели монаду
67
Сухой итог
●
Функциональное программирование это просто
●
Можно использовать привычные приемы, но
проще
●
Можно заимствовать из функционального
программирования хитрые приемы
●
Монады совсем не страшные
68
Полезности
http://www.intuit.ru/studies/courses/471/327/info
https://github.com/fsprojects/FSharpx.Extras
https://github.com/jack-pappas/ExtCore/
http://fsharpforfunandprofit.com/
69
Вопросы?
70

Теории и практики функционального программирования.