SlideShare a Scribd company logo
1 of 60
Download to read offline
Игорь Кашкута
Перспективы
функционального подхода
2ГИС
Сложность
Проблема
“Нужно стремиться 

к простоте”
Системный подход

к упрощению кода
Решение
Идеи Неизменяемость
Чистота
Functional Reactive Programming
Неизменяемость
Immutability
@interface ContactModel : NSObject
@property NSUInteger age;
@property NSString *name;
@property NSString *surname;
@property NSArray *children;
@property Organization *company;
@end
Неизменяемость
Неизменяемость
NSArray *contacts = LoadContactsFromDB();
...
ShowInGUI(contacts);
...
AsyncBackupToCloud(contacts);
...
AsyncFetchNewContacts(contacts);
...
NSLog(@"%@", contacts);//??
Неизменяемость
NSArray *contacts = LoadContactsFromDB();
...
NSLog(@"%@", contacts);//Original contacts
NSLog(@"%@", contacts);//Contacts with anotherName
//In another thread
[contacts[idx] setName:anotherName];
• Состояние объекта нельзя изменить 

после его создания

• Мутация неизменяемых объектов — 

создание новых, Copy-On-Write
Неизменяемые объекты
@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
Неизменяемость
Неизменяемость
//Copy-On-Write setter
-(ContactModel *)cowSetAge:(NSUInteger)age {
return [ContactModel modelWithAge:age
name:self.name
surname:self.surname
children:self.children
company:self.company];
}
Неизменяемость
NSArray *contacts = LoadContactsFromDB();
...
ShowInGUI(contacts);
...
AsyncBackupToCloud(contacts);
...
AsyncFetchNewContacts(contacts);
...
NSLog(@"%@", contacts);//Always the same!
[contacts[idx] setName:anotherName];//Error!
• Потокобезопасность бесплатно

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

• Отсутствуют наблюдаемые сайд-эффекты
Чистые функции
// Конкатенация строк — чистая функция
NSString *CombineStrings(NSString *l, NSString *r);
// Любая математическая операция тоже чистая
int sum(int a, int b);
@interface Collection : NSObject
// Нечистая функция — нет аргументов и
// возвращаемое значение каждый раз разное.
- (id)next;
@end
Чистые функции
Нечистые функции порой удивляют 

и вызывают паранойю.
Нечистые функции
• Простота тестирования
• Предсказуемость
Чистые функции
Functional Reactive

Programming
• Набор неизменяемых значений
одного типа во времени
• Может закончится успешно или с
ошибкой
• Может и вовсе не иметь значений
Поток
• Создание потоков
• Преобразования одних в другие
• Подписка на значения
FRP Frameworks
UITextField *field = [UITextField new];
[field.rac_textSignal
subscribeNext:^(NSString *text) {
NSLog(@"Current text: %@", text);
}];
Text Field как поток
@“H”
Current text: H
UITextField *field = [UITextField new];
[field.rac_textSignal
subscribeNext:^(NSString *text) {
NSLog(@"Current text: %@", text);
}];
Text Field как поток
@“H”
@“He”
Current text: H
Current text: He
UITextField *field = [UITextField new];
[field.rac_textSignal
subscribeNext:^(NSString *text) {
NSLog(@"Current text: %@", text);
}];
Text Field как поток
@“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 как поток
@“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 как поток
@“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 как поток
Button
Кнопка как поток
Button
UIControlEventTouchDown
Кнопка как поток
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
Кнопка как поток
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
Кнопка как поток
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
UIControlEventTouchDragOutside
Кнопка как поток
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
UIControlEventTouchDragOutside
UIControlEventTouchDragEnter
Кнопка как поток
Button
UIControlEventTouchDown
UIControlEventTouchDragInside
UIControlEventTouchDragExit
UIControlEventTouchDragOutside
UIControlEventTouchDragEnter
UIControlEventTouchUpInside
Кнопка как поток
Дом
Автобус
Экспоцентр
Перчини
Дом
[locationManager.locationSignal
subscribeNext:^(CLLocation *loc) {
NSLog(@"New location: %@", loc);
}];
Location Manager как поток
request
result
request completed
[[APIClient fetchDataForUser:user]
subscribeNext:^(NSData *result) {
NSLog(@“Result: %@", result);
}
error:^(NSError *error) {
NSLog(@"Error: %@", error);
}
completed:^{
NSLog(@"Request completed");
}];
Запрос в Сеть тоже поток
error
request
[[APIClient fetchDataForUser:user]
subscribeNext:^(NSData *result) {
NSLog(@“Result: %@", result);
}
error:^(NSError *error) {
NSLog(@"Error: %@", error);
}
completed:^{
NSLog(@"Request completed");
}];
Запрос в Сеть тоже поток
• Разные с виду сущности можно представить в
виде потока
• Поток представляет состояние — прошлое,
настоящее, будущее
Поток
• Комбинирование
• Преобразование значений
• Планирование выполнения на других тредах
• И много всего другого!
Операции над потоками
Комбинация потоков
[[RACSignal merge:@[
[client fetchMyTweets],
[client fetchTweetsForHashtag:@“codefest”]
]]
subscribeNext:^(Tweet *newTweet){
//Показ newTweet в UI
}];
Комбинация потоков
Преобразование значений
[[RACObserve(urlBarVM, text)
map:^(NSString *urlFromUser) {
return NormalizeURL(urlFromUser);
}]
subscribeNext:^(NSURL *url) {
@strongify(self);
[self.loadWebPageCommand execute:url];
}];
Преобразование значений
Преобразование значений
[[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];
}];
Преобразование значений
[[[[refreshButton.tapSignal
deliverOn:RACScheduler.scheduler]
map:^(id _){
NSLog(@“Fetching image in background thread..”);
return SyncLoadImageFromNetwork();
}]
deliverOnMainThread]
subscribeNext:^(UIImage *img) {
[self.imageView setImage:img];
}];
Планирование на другие треды
Сайд-эффекты
[[[[[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];
}];
Реактивное присваивание
//Неважно, как именно начался поиск, но после его
//начала фокус с поисковой строки надо убрать.
RAC(self, topBarVM.textFieldVM.focused) =
[self.searchVM.searchDidStartSignal mapReplace:@NO];
• В четыре раза лучше коллбэков
• В два раза лучше промисов
• Способствуют локальности кода
• Упрощают обработку ошибок в цепочках
• Избавляют от Callback Hell
Потоки — это монады
[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
Оператор FlatMap
[[[[[[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
• Неизменяемые объекты — значения в потоке
• Потоки изолируют состояние
• Операторы — чистые функции
• Операторы изолируют взаимосвязи
Functional Reactive Programming
• Прозрачность кода
• Единообразие в работе с разными сущностями
• Простота асинхронного программирования
• Описание “что” надо сделать, вместо “как”
FRP на практике
• Память/производительность
• Большой стек вызовов
• Трудно построчно отлаживать
• Нет готовых специалистов
Цена
Повторим Неизменяемость
Чистота
Functional Reactive Programming
Всё это доступно
вам уже сейчас!
Что дальше The Reactive Manifesto
ReactiveCocoa/RxJava/Rx
Clojure/Rich Hickey
Haskell
Спасибо!
@ikashkutaИгорь Кашкута
i.kashkuta@2gis.ru

More Related Content

What's hot

2.4 Использование указателей
2.4 Использование указателей2.4 Использование указателей
2.4 Использование указателейDEVTYPE
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Vasya Petrov
 
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSКурсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSГлеб Тарасов
 
AlgoCollections (RUS)
AlgoCollections (RUS)AlgoCollections (RUS)
AlgoCollections (RUS)Anton Bukov
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Ontico
 
Kirill Zotin клиент серверное взаимодействие под android в деталях
Kirill Zotin клиент серверное взаимодействие под android в деталяхKirill Zotin клиент серверное взаимодействие под android в деталях
Kirill Zotin клиент серверное взаимодействие под android в деталяхDneprCiklumEvents
 
MongoDB - About Performance Optimization, Ivan Griga - Smart Gamma
MongoDB - About Performance Optimization, Ivan Griga - Smart GammaMongoDB - About Performance Optimization, Ivan Griga - Smart Gamma
MongoDB - About Performance Optimization, Ivan Griga - Smart GammaEvgeniy Kuzmin
 
Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Alexander Granin
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6Dmitry Soshnikov
 
Python dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущееPython dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущееdelimitry
 
Groovy и Grails. Быстро и обо всём
Groovy и Grails. Быстро и обо всёмGroovy и Grails. Быстро и обо всём
Groovy и Grails. Быстро и обо всёмRuslan Balkin
 
Быть в 10 раз эффективнее благодаря Groovy
Быть в 10 раз эффективнее благодаря GroovyБыть в 10 раз эффективнее благодаря Groovy
Быть в 10 раз эффективнее благодаря GroovyEvgeny Kompaniyets
 
Web internship java script
Web internship   java scriptWeb internship   java script
Web internship java scriptNoveo
 
Как писать под Android программы, а не код
Как писать под Android программы, а не кодКак писать под Android программы, а не код
Как писать под Android программы, а не код0leGG
 
Александр Щепановский «Почему каждому языку нужен свой _»
Александр Щепановский «Почему каждому языку нужен свой _»Александр Щепановский «Почему каждому языку нужен свой _»
Александр Щепановский «Почему каждому языку нужен свой _»DevDay
 
Why Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreWhy Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreAlexander Schepanovski
 

What's hot (20)

Javascript
JavascriptJavascript
Javascript
 
2.4 Использование указателей
2.4 Использование указателей2.4 Использование указателей
2.4 Использование указателей
 
Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSКурсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
 
AlgoCollections (RUS)
AlgoCollections (RUS)AlgoCollections (RUS)
AlgoCollections (RUS)
 
Основы языка R
Основы языка RОсновы языка R
Основы языка R
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
 
Kirill Zotin клиент серверное взаимодействие под android в деталях
Kirill Zotin клиент серверное взаимодействие под android в деталяхKirill Zotin клиент серверное взаимодействие под android в деталях
Kirill Zotin клиент серверное взаимодействие под android в деталях
 
MongoDB - About Performance Optimization, Ivan Griga - Smart Gamma
MongoDB - About Performance Optimization, Ivan Griga - Smart GammaMongoDB - About Performance Optimization, Ivan Griga - Smart Gamma
MongoDB - About Performance Optimization, Ivan Griga - Smart Gamma
 
msumobi2. Лекция 1
msumobi2. Лекция 1msumobi2. Лекция 1
msumobi2. Лекция 1
 
Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++Функционально декларативный дизайн на C++
Функционально декларативный дизайн на C++
 
Algo 00
Algo 00Algo 00
Algo 00
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6
 
Python dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущееPython dict: прошлое, настоящее, будущее
Python dict: прошлое, настоящее, будущее
 
Groovy и Grails. Быстро и обо всём
Groovy и Grails. Быстро и обо всёмGroovy и Grails. Быстро и обо всём
Groovy и Grails. Быстро и обо всём
 
Быть в 10 раз эффективнее благодаря Groovy
Быть в 10 раз эффективнее благодаря GroovyБыть в 10 раз эффективнее благодаря Groovy
Быть в 10 раз эффективнее благодаря Groovy
 
Web internship java script
Web internship   java scriptWeb internship   java script
Web internship java script
 
Как писать под Android программы, а не код
Как писать под Android программы, а не кодКак писать под Android программы, а не код
Как писать под Android программы, а не код
 
Александр Щепановский «Почему каждому языку нужен свой _»
Александр Щепановский «Почему каждому языку нужен свой _»Александр Щепановский «Почему каждому языку нужен свой _»
Александр Щепановский «Почему каждому языку нужен свой _»
 
Why Every Language Needs Its Underscore
Why Every Language Needs Its UnderscoreWhy Every Language Needs Its Underscore
Why Every Language Needs Its Underscore
 

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

Functional Reactive Programming
Functional Reactive ProgrammingFunctional Reactive Programming
Functional Reactive ProgrammingSerg Buglakov
 
Школа-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиШкола-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиГлеб Тарасов
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseCocoaHeads
 
Экскурсия по Flutter SDK
Экскурсия по Flutter SDKЭкскурсия по Flutter SDK
Экскурсия по Flutter SDKSergey Penkovsky
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + RetrofitDev2Dev
 
Active Record for CoreData
Active Record for CoreDataActive Record for CoreData
Active Record for CoreDataDmitriy Kuragin
 
хранение данных
хранение данныххранение данных
хранение данныхNoveo
 
2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭ2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭOleg Parinov
 
Kvc, kvo
Kvc, kvoKvc, kvo
Kvc, kvoNoveo
 
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Yandex
 
Java осень 2012 лекция 8
Java осень 2012 лекция 8Java осень 2012 лекция 8
Java осень 2012 лекция 8Technopark
 
Как программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногуКак программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногуAndreyGeonya
 
Работа с БД в Java
Работа с БД в JavaРабота с БД в Java
Работа с БД в Javametaform
 
Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...
Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...
Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...Minsk Linux User Group
 
Groovy presentation.
Groovy presentation.Groovy presentation.
Groovy presentation.Infinity
 
Курсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с даннымиКурсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с даннымиГлеб Тарасов
 
«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...
«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...
«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...Анастасия Вязьмина
 
ES2015+: давно пора!
ES2015+: давно пора!ES2015+: давно пора!
ES2015+: давно пора!Vitebsk Miniq
 
02 ns string
02   ns string02   ns string
02 ns stringNoveo
 

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

Functional Reactive Programming
Functional Reactive ProgrammingFunctional Reactive Programming
Functional Reactive Programming
 
Школа-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с даннымиШкола-студия разработки для iOS. Лекция 4. Работа с данными
Школа-студия разработки для iOS. Лекция 4. Работа с данными
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap Database
 
Экскурсия по Flutter SDK
Экскурсия по Flutter SDKЭкскурсия по Flutter SDK
Экскурсия по Flutter SDK
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + Retrofit
 
Active Record for CoreData
Active Record for CoreDataActive Record for CoreData
Active Record for CoreData
 
хранение данных
хранение данныххранение данных
хранение данных
 
2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭ2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭ
 
Kvc, kvo
Kvc, kvoKvc, kvo
Kvc, kvo
 
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
Хранение данных в iPhone. (FMDB, SQL-Persistence, CoreData)
 
Js fuckworks
Js fuckworksJs fuckworks
Js fuckworks
 
Java осень 2012 лекция 8
Java осень 2012 лекция 8Java осень 2012 лекция 8
Java осень 2012 лекция 8
 
Как программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногуКак программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногу
 
Работа с БД в Java
Работа с БД в JavaРабота с БД в Java
Работа с БД в Java
 
Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...
Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...
Дмитрий Костюк - Очаровательные серые кнопки: обзор эволюции виджет-тулкитовH...
 
Groovy presentation.
Groovy presentation.Groovy presentation.
Groovy presentation.
 
Курсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с даннымиКурсы по мобильной разработке под iOS. 5 лекция. Работа с данными
Курсы по мобильной разработке под iOS. 5 лекция. Работа с данными
 
«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...
«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...
«Облачный» сервис интеллектуального анализа данных. графический интерфейс пос...
 
ES2015+: давно пора!
ES2015+: давно пора!ES2015+: давно пора!
ES2015+: давно пора!
 
02 ns string
02   ns string02   ns string
02 ns string
 

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