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 -> string20

Композиция

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 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

Мемоизация#практика

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


Recommended