60
Игорь Кашкута Перспективы функционального подхода 2ГИС

Перспективы функционального подхода

Embed Size (px)

Citation preview

Page 1: Перспективы функционального подхода

Игорь Кашкута

Перспективы функционального подхода

2ГИС

Page 2: Перспективы функционального подхода

Сложность

Проблема

Page 3: Перспективы функционального подхода

“Нужно стремиться к простоте”

Page 4: Перспективы функционального подхода

Системный подходк упрощению кода

Решение

Page 5: Перспективы функционального подхода

Идеи Неизменяемость Чистота Functional Reactive Programming

Page 6: Перспективы функционального подхода

НеизменяемостьImmutability

Page 7: Перспективы функционального подхода

@interface ContactModel : NSObject @property NSUInteger age; @property NSString *name; @property NSString *surname; @property NSArray *children; @property Organization *company; @end

Неизменяемость

Page 8: Перспективы функционального подхода

Неизменяемость

NSArray *contacts = LoadContactsFromDB(); ... ShowInGUI(contacts); ... AsyncBackupToCloud(contacts); ... AsyncFetchNewContacts(contacts); ... NSLog(@"%@", contacts);//??

Page 9: Перспективы функционального подхода

Неизменяемость

NSArray *contacts = LoadContactsFromDB(); ... NSLog(@"%@", contacts);//Original contacts NSLog(@"%@", contacts);//Contacts with anotherName

//In another thread [contacts[idx] setName:anotherName];

Page 10: Перспективы функционального подхода

• Состояние объекта нельзя изменить после его создания

• Мутация неизменяемых объектов — создание новых, Copy-On-Write

Неизменяемые объекты

Page 11: Перспективы функционального подхода

@interface ContactModel : NSObject @property (readonly) NSUInteger age; @property (readonly) NSString *name; @property (readonly) NSString *surname; @property (readonly) NSArray *children; @property (readonly) Organization *company; //Class methods for object creation @end

Неизменяемость

Page 12: Перспективы функционального подхода

Неизменяемость

//Copy-On-Write setter -(ContactModel *)cowSetAge:(NSUInteger)age { return [ContactModel modelWithAge:age name:self.name surname:self.surname children:self.children company:self.company]; }

Page 13: Перспективы функционального подхода

Неизменяемость

NSArray *contacts = LoadContactsFromDB(); ... ShowInGUI(contacts); ... AsyncBackupToCloud(contacts); ... AsyncFetchNewContacts(contacts); ... NSLog(@"%@", contacts);//Always the same!

[contacts[idx] setName:anotherName];//Error!

Page 14: Перспективы функционального подхода

• Потокобезопасность бесплатно

• Предсказуемость. Неизменяемые объекты всегда в консистентном состоянии

Неизменяемость

Page 15: Перспективы функционального подхода

ЧистотаPurity

Page 16: Перспективы функционального подхода

• Одинаковые аргументы — одинаковый результат

• Отсутствуют наблюдаемые сайд-эффекты

Чистые функции

Page 17: Перспективы функционального подхода

// Конкатенация строк — чистая функция NSString *CombineStrings(NSString *l, NSString *r);

// Любая математическая операция тоже чистая int sum(int a, int b);

@interface Collection : NSObject // Нечистая функция — нет аргументов и // возвращаемое значение каждый раз разное. - (id)next; @end

Чистые функции

Page 18: Перспективы функционального подхода

Нечистые функции порой удивляют и вызывают паранойю.

Нечистые функции

Page 19: Перспективы функционального подхода

• Простота тестирования • Предсказуемость

Чистые функции

Page 20: Перспективы функционального подхода

Functional Reactive Programming

Page 21: Перспективы функционального подхода

• Набор неизменяемых значений одного типа во времени

• Может закончится успешно или с ошибкой

• Может и вовсе не иметь значений

Поток

Page 22: Перспективы функционального подхода

• Создание потоков

• Преобразования одних в другие

• Подписка на значения

FRP Frameworks

Page 23: Перспективы функционального подхода

UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];

Text Field как поток

Page 24: Перспективы функционального подхода

@“H”

Current text: H

UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];

Text Field как поток

Page 25: Перспективы функционального подхода

@“H”

@“He”

Current text: H

Current text: He

UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];

Text Field как поток

Page 26: Перспективы функционального подхода

@“H”

@“He”

@“Hel”Current text: H

Current text: He

Current text: Hel

UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];

Text Field как поток

Page 27: Перспективы функционального подхода

@“H”

@“He”

@“Hel”

@“Hell”Current text: H

Current text: He

Current text: Hel

Current text: Hell

UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];

Text Field как поток

Page 28: Перспективы функционального подхода

@“H”

@“He”

@“Hel”

@“Hell”

@“Hello”

Current text: H

Current text: He

Current text: Hel

Current text: Hell

Current text: Hello

UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];

Text Field как поток

Page 29: Перспективы функционального подхода

Button

Кнопка как поток

Page 30: Перспективы функционального подхода

Button

UIControlEventTouchDown

Кнопка как поток

Page 31: Перспективы функционального подхода

Button

UIControlEventTouchDown

UIControlEventTouchDragInside

Кнопка как поток

Page 32: Перспективы функционального подхода

Button

UIControlEventTouchDown

UIControlEventTouchDragInside

UIControlEventTouchDragExit

Кнопка как поток

Page 33: Перспективы функционального подхода

Button

UIControlEventTouchDown

UIControlEventTouchDragInside

UIControlEventTouchDragExit

UIControlEventTouchDragOutside

Кнопка как поток

Page 34: Перспективы функционального подхода

Button

UIControlEventTouchDown

UIControlEventTouchDragInside

UIControlEventTouchDragExit

UIControlEventTouchDragOutside

UIControlEventTouchDragEnter

Кнопка как поток

Page 35: Перспективы функционального подхода

Button

UIControlEventTouchDown

UIControlEventTouchDragInside

UIControlEventTouchDragExit

UIControlEventTouchDragOutside

UIControlEventTouchDragEnter

UIControlEventTouchUpInside

Кнопка как поток

Page 36: Перспективы функционального подхода

Дом

Автобус

Экспоцентр

Перчини

Дом

[locationManager.locationSignal subscribeNext:^(CLLocation *loc) { NSLog(@"New location: %@", loc); }];

Location Manager как поток

Page 37: Перспективы функционального подхода

request

result

request completed

[[APIClient fetchDataForUser:user] subscribeNext:^(NSData *result) { NSLog(@“Result: %@", result); } error:^(NSError *error) { NSLog(@"Error: %@", error); } completed:^{ NSLog(@"Request completed"); }];

Запрос в Сеть тоже поток

Page 38: Перспективы функционального подхода

error

request

[[APIClient fetchDataForUser:user] subscribeNext:^(NSData *result) { NSLog(@“Result: %@", result); } error:^(NSError *error) { NSLog(@"Error: %@", error); } completed:^{ NSLog(@"Request completed"); }];

Запрос в Сеть тоже поток

Page 39: Перспективы функционального подхода

• Разные с виду сущности можно представить в виде потока

• Поток представляет состояние — прошлое, настоящее, будущее

Поток

Page 40: Перспективы функционального подхода

• Комбинирование • Преобразование значений • Планирование выполнения на других тредах • И много всего другого!

Операции над потоками

Page 41: Перспективы функционального подхода

Комбинация потоков

Page 42: Перспективы функционального подхода

[[RACSignal merge:@[ [client fetchMyTweets], [client fetchTweetsForHashtag:@“codefest”] ]] subscribeNext:^(Tweet *newTweet){ //Показ newTweet в UI }];

Комбинация потоков

Page 43: Перспективы функционального подхода

Преобразование значений

Page 44: Перспективы функционального подхода

[[RACObserve(urlBarVM, text) map:^(NSString *urlFromUser) { return NormalizeURL(urlFromUser); }] subscribeNext:^(NSURL *url) { @strongify(self); [self.loadWebPageCommand execute:url]; }];

Преобразование значений

Page 45: Перспективы функционального подхода

Преобразование значений

Page 46: Перспективы функционального подхода

[[RACSignal combineLatest:@[ RACObserve(self, password), RACObserve(self, passwordConfirmation) ] reduce:^(NSString *currentPassword, NSString *currentConfirmPassword) { return @([currentConfirmPassword isEqualToString: currentPassword]); }] subscribeNext:^(NSNumber *passwordsMatch) { @strongify(self); self.createButton.enabled = [passwordsMatch boolValue]; }];

Преобразование значений

Page 47: Перспективы функционального подхода

[[[[refreshButton.tapSignal deliverOn:RACScheduler.scheduler] map:^(id _){ NSLog(@“Fetching image in background thread..”); return SyncLoadImageFromNetwork(); }] deliverOnMainThread] subscribeNext:^(UIImage *img) { [self.imageView setImage:img]; }];

Планирование на другие треды

Page 48: Перспективы функционального подхода

Сайд-эффекты[[[[[refreshButton.tapSignal deliverOn:RACScheduler.scheduler] map:^(id _){ NSLog(@“Fetching image in background thread..”); return SyncLoadImageFromNetwork(); }] doNext:^(UIImage *img){ SaveImageToDisk(img); }] deliverOnMainThread] subscribeNext:^(UIImage *img) { [self.imageView setImage:img]; }];

Page 49: Перспективы функционального подхода

Реактивное присваивание

//Неважно, как именно начался поиск, но после его //начала фокус с поисковой строки надо убрать. RAC(self, topBarVM.textFieldVM.focused) = [self.searchVM.searchDidStartSignal mapReplace:@NO];

Page 50: Перспективы функционального подхода

• В четыре раза лучше коллбэков • В два раза лучше промисов • Способствуют локальности кода • Упрощают обработку ошибок в цепочках • Избавляют от Callback Hell

Потоки — это монады

Page 51: Перспективы функционального подхода

[task1 setCompleted:^(id result1){ [task2 setCompleted:^(id result2){ [task3 setCompleted^(id result3){ [task4 setCompleted:^(id result4){ [task5 setCompleted:^(id result5){ //Сделать что-то с result5 NSLog(@"Ура! %@", result5); }]; [task5 start]; }]; [task4 start]; }]; [task3 start]; }]; [task2 start]; }]; [task1 start];

Callback Hell

Page 52: Перспективы функционального подхода

Оператор FlatMap

Page 53: Перспективы функционального подхода

[[[[[[client fetchTask1] flattenMap:^(id result1) { return [client fetchTask2]; }] flattenMap:^(id result2) { return [client fetchTask3]; }] flattenMap:^(id result3) { return [client fetchTask4]; }] flattenMap:^(id result4) { return [client fetchTask5]; }] subscribeNext:^(id result5){ NSLog(@“Ура! %@”, result5); } error:^(NSError *error){ //Do error processing for any task }];

Оператор FlatMap

Page 54: Перспективы функционального подхода

• Неизменяемые объекты — значения в потоке • Потоки изолируют состояние • Операторы — чистые функции • Операторы изолируют взаимосвязи

Functional Reactive Programming

Page 55: Перспективы функционального подхода

• Прозрачность кода • Единообразие в работе с разными сущностями • Простота асинхронного программирования • Описание “что” надо сделать, вместо “как”

FRP на практике

Page 56: Перспективы функционального подхода

• Память/производительность • Большой стек вызовов • Трудно построчно отлаживать • Нет готовых специалистов

Цена

Page 57: Перспективы функционального подхода

Повторим Неизменяемость Чистота Functional Reactive Programming

Page 58: Перспективы функционального подхода

Всё это доступно вам уже сейчас!

Page 60: Перспективы функционального подхода

Спасибо!

@ikashkutaИгорь Кашкута[email protected]