Кто я?
5+ лет dotNet interpraise/
2+ лет python freelance/
2+ I love FP
докладчик dotnetconf, dev2dev/
организатор it-сообщества dev2dev
2
К чему это все?
● ФП устарело.● ФП применимо только в узком круге задач.
● Я БОЮСЬ ФУНКЦИОНАЛЬНОГО ПРОГРАММИРОВАНИЯ!
9
ООП против ФП● 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
Функции это тоже значения
let getCalc f x y = fun z -> (f x) + y + z
let calc = getCalc toX10 1 2
let result = calc value
17
Композиция : функции
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 -> string20
Композиция : функции #каррирование
let add x y = x + y // int -> int -> int
let add2 = add 2 // int -> int
let res = add2 2 // val res : int = 4
27
Типы#обзор
type DiscriminatedUnion = | Variant1 of int | Variant2 of string
type Enum = | One = 1 | Two = 2 | Three = 3
31
Композиция : типы
type NodeName = stringtype UID = int
type HierarchyIdentifier = | Plain of UID | Complex of NodeName*HierarchyIdentifier
33
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
Мемоизация#чистыефункции
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 adder49
Мемоизация#ультракеширование
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 adder50
Мемоизация#ультракеширование
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 adder51
Обработка ошибок #идеальныймир
let handleRequest = readRequest >> readEntityFromDB >> modifyEntity >> createTaskFromEntity >> writeTaskToMQ >> createResultMessage >> sendMessage
56
Обработка ошибок 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
Обработка ошибок#неработает
'a → Result<'b>
» 'b → Result<'c>
» 'c → Result<'d>
» …
Типы не совпадают...
61
Обработка ошибок 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
Сухой итог
● Функциональное программирование это просто● Можно использовать привычные приемы, но
проще● Можно заимствовать из функционального
программирования хитрые приемы● Монады совсем не страшные
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