Перспективы функционального подхода
TRANSCRIPT
![Page 1: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/1.jpg)
Игорь Кашкута
Перспективы функционального подхода
2ГИС
![Page 2: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/2.jpg)
Сложность
Проблема
![Page 3: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/3.jpg)
“Нужно стремиться к простоте”
![Page 4: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/4.jpg)
Системный подходк упрощению кода
Решение
![Page 5: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/5.jpg)
Идеи Неизменяемость Чистота Functional Reactive Programming
![Page 6: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/6.jpg)
НеизменяемостьImmutability
![Page 7: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/7.jpg)
@interface ContactModel : NSObject @property NSUInteger age; @property NSString *name; @property NSString *surname; @property NSArray *children; @property Organization *company; @end
Неизменяемость
![Page 8: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/8.jpg)
Неизменяемость
NSArray *contacts = LoadContactsFromDB(); ... ShowInGUI(contacts); ... AsyncBackupToCloud(contacts); ... AsyncFetchNewContacts(contacts); ... NSLog(@"%@", contacts);//??
![Page 9: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/9.jpg)
Неизменяемость
NSArray *contacts = LoadContactsFromDB(); ... NSLog(@"%@", contacts);//Original contacts NSLog(@"%@", contacts);//Contacts with anotherName
//In another thread [contacts[idx] setName:anotherName];
![Page 10: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/10.jpg)
• Состояние объекта нельзя изменить после его создания
• Мутация неизменяемых объектов — создание новых, Copy-On-Write
Неизменяемые объекты
![Page 11: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/11.jpg)
@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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/12.jpg)
Неизменяемость
//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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/13.jpg)
Неизменяемость
NSArray *contacts = LoadContactsFromDB(); ... ShowInGUI(contacts); ... AsyncBackupToCloud(contacts); ... AsyncFetchNewContacts(contacts); ... NSLog(@"%@", contacts);//Always the same!
[contacts[idx] setName:anotherName];//Error!
![Page 14: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/14.jpg)
• Потокобезопасность бесплатно
• Предсказуемость. Неизменяемые объекты всегда в консистентном состоянии
Неизменяемость
![Page 15: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/15.jpg)
ЧистотаPurity
![Page 16: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/16.jpg)
• Одинаковые аргументы — одинаковый результат
• Отсутствуют наблюдаемые сайд-эффекты
Чистые функции
![Page 17: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/17.jpg)
// Конкатенация строк — чистая функция NSString *CombineStrings(NSString *l, NSString *r);
// Любая математическая операция тоже чистая int sum(int a, int b);
@interface Collection : NSObject // Нечистая функция — нет аргументов и // возвращаемое значение каждый раз разное. - (id)next; @end
Чистые функции
![Page 18: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/18.jpg)
Нечистые функции порой удивляют и вызывают паранойю.
Нечистые функции
![Page 19: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/19.jpg)
• Простота тестирования • Предсказуемость
Чистые функции
![Page 20: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/20.jpg)
Functional Reactive Programming
![Page 21: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/21.jpg)
• Набор неизменяемых значений одного типа во времени
• Может закончится успешно или с ошибкой
• Может и вовсе не иметь значений
Поток
![Page 22: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/22.jpg)
• Создание потоков
• Преобразования одних в другие
• Подписка на значения
FRP Frameworks
![Page 23: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/23.jpg)
UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];
Text Field как поток
![Page 24: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/24.jpg)
@“H”
Current text: H
UITextField *field = [UITextField new]; [field.rac_textSignal subscribeNext:^(NSString *text) { NSLog(@"Current text: %@", text); }];
Text Field как поток
![Page 25: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/25.jpg)
@“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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/26.jpg)
@“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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/27.jpg)
@“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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/28.jpg)
@“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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/29.jpg)
Button
Кнопка как поток
![Page 30: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/30.jpg)
Button
UIControlEventTouchDown
Кнопка как поток
![Page 31: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/31.jpg)
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
Кнопка как поток
![Page 32: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/32.jpg)
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
Кнопка как поток
![Page 33: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/33.jpg)
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
UIControlEventTouchDragOutside
Кнопка как поток
![Page 34: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/34.jpg)
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
UIControlEventTouchDragOutside
UIControlEventTouchDragEnter
Кнопка как поток
![Page 35: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/35.jpg)
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
UIControlEventTouchDragOutside
UIControlEventTouchDragEnter
UIControlEventTouchUpInside
Кнопка как поток
![Page 36: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/36.jpg)
Дом
Автобус
Экспоцентр
Перчини
Дом
[locationManager.locationSignal subscribeNext:^(CLLocation *loc) { NSLog(@"New location: %@", loc); }];
Location Manager как поток
![Page 37: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/37.jpg)
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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/38.jpg)
error
request
[[APIClient fetchDataForUser:user] subscribeNext:^(NSData *result) { NSLog(@“Result: %@", result); } error:^(NSError *error) { NSLog(@"Error: %@", error); } completed:^{ NSLog(@"Request completed"); }];
Запрос в Сеть тоже поток
![Page 39: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/39.jpg)
• Разные с виду сущности можно представить в виде потока
• Поток представляет состояние — прошлое, настоящее, будущее
Поток
![Page 40: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/40.jpg)
• Комбинирование • Преобразование значений • Планирование выполнения на других тредах • И много всего другого!
Операции над потоками
![Page 41: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/41.jpg)
Комбинация потоков
![Page 42: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/42.jpg)
[[RACSignal merge:@[ [client fetchMyTweets], [client fetchTweetsForHashtag:@“codefest”] ]] subscribeNext:^(Tweet *newTweet){ //Показ newTweet в UI }];
Комбинация потоков
![Page 43: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/43.jpg)
Преобразование значений
![Page 44: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/44.jpg)
[[RACObserve(urlBarVM, text) map:^(NSString *urlFromUser) { return NormalizeURL(urlFromUser); }] subscribeNext:^(NSURL *url) { @strongify(self); [self.loadWebPageCommand execute:url]; }];
Преобразование значений
![Page 45: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/45.jpg)
Преобразование значений
![Page 46: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/46.jpg)
[[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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/47.jpg)
[[[[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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/48.jpg)
Сайд-эффекты[[[[[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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/49.jpg)
Реактивное присваивание
//Неважно, как именно начался поиск, но после его //начала фокус с поисковой строки надо убрать. RAC(self, topBarVM.textFieldVM.focused) = [self.searchVM.searchDidStartSignal mapReplace:@NO];
![Page 50: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/50.jpg)
• В четыре раза лучше коллбэков • В два раза лучше промисов • Способствуют локальности кода • Упрощают обработку ошибок в цепочках • Избавляют от Callback Hell
Потоки — это монады
![Page 51: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/51.jpg)
[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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/52.jpg)
Оператор FlatMap
![Page 53: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/53.jpg)
[[[[[[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: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/54.jpg)
• Неизменяемые объекты — значения в потоке • Потоки изолируют состояние • Операторы — чистые функции • Операторы изолируют взаимосвязи
Functional Reactive Programming
![Page 55: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/55.jpg)
• Прозрачность кода • Единообразие в работе с разными сущностями • Простота асинхронного программирования • Описание “что” надо сделать, вместо “как”
FRP на практике
![Page 56: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/56.jpg)
• Память/производительность • Большой стек вызовов • Трудно построчно отлаживать • Нет готовых специалистов
Цена
![Page 57: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/57.jpg)
Повторим Неизменяемость Чистота Functional Reactive Programming
![Page 58: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/58.jpg)
Всё это доступно вам уже сейчас!
![Page 59: Перспективы функционального подхода](https://reader030.vdocuments.site/reader030/viewer/2022032618/55b4feedbb61ebce1d8b47a8/html5/thumbnails/59.jpg)
Что дальше The Reactive Manifesto ReactiveCocoa/RxJava/Rx Clojure/Rich Hickey Haskell