Share
Embed
Transcript
60 slides
-
Functional Reactive Programming (FRP) is a declarative programming paradigm for
working with mutable time- varying values. FRP -
Functional Reactive Programming (FRP) is a declarative programming paradigm for
working with mutable time- varying values. Declarative – is about “what” not “how” to accomplish. Unlike imperative approach, system takes care of “how”. FRP -
What and How What and how NSArray *words = ...;
for (NSUInteger index = 0; index < words.count; index++) { NSString *word = words[index]; // do stuff } -
What and How What and how NSArray *words = ...;
for (NSUInteger index = 0; index < words.count; index++) { NSString *word = words[index]; // do stuff } What NSArray *words = ...; for (NSString *word in words) { // do stuff } -
What and How What and how NSMutableArray *uppercasedWords = [NSMutableArray
array]; for (NSString *word in words) { [uppercasedWords addObject:word.uppercaseString]; } -
What and How What and how NSMutableArray *uppercasedWords = [NSMutableArray
array]; for (NSString *word in words) { [uppercasedWords addObject:word.uppercaseString]; } What NSArray *uppercasedWords = [words map:^(NSString *word) { return word.uppercaseString; }]; -
Key concepts Avoids state and mutable data Any computation is
mathematical function Functional Programming -
State BOOL walksLikeADuck; // 2 states BOOL quacksLikeADuck; // 4
states BOOL isProbablyADuck; // 8 states BOOL maybeItCanFly; // 16 states Complexity is STATE -
Minimize state by deriving it State BOOL walksLikeADuck; BOOL quacksLikeADuck;
BOOL isProbablyADuck; -
Minimize state by deriving it State BOOL walksLikeADuck; BOOL quacksLikeADuck;
BOOL isProbablyADuck; BOOL walksLikeADuck; // 2 states BOOL quacksLikeADuck; // 4 states BOOL isProbablyADuck; // 4 states -
Functions Comparison Imperative function has side effects that can change
program state behaves differently depending on program state -
Functions Comparison Imperative function has side effects that can change
program state behaves differently depending on program state Mathematical function result of a function depends only on the arguments easier to understand and predict the behavior of a program -
Reactive programming – programming paradigm oriented around data flows and
the propagation of changes. Reactive Programming -
Reactive programming – programming paradigm oriented around data flows and
the propagation of changes. Reactive Programming Data flows can be either static or dynamic. -
Data Changes Propagation Imperative approach b = 10 c =
11 a = b + c (21) -
Data Changes Propagation Imperative approach b = 10 c =
11 a = b + c (21) b = 11 a = ? -
Data Changes Propagation Imperative approach b = 10 c =
11 a = b + c (21) b = 11 a = ? (21) -
Data Changes Propagation Imperative approach b = 10 c =
11 a = b + c (21) b = 11 a = ? (21) Reactive approach b = 10 c = 11 a = b + c (21) b = 11 a = ? (22) -
Why not KVO? Key Value Observing – mechanism that allows
objects to be notified of changes to specified properties of other objects. -
Why not KVO? Key Value Observing – mechanism that allows
objects to be notified of changes to specified properties of other objects. not easy to use lots of unused parameters hard to maintain multithreading no block-based API -
Why not KVO? // Some controller or else [dog addObserver:me
forKeyPath:@"hunger" options:NSKeyValueObservingOptionNew context:NULL]; // . . . [dog removeObserver:me forKeyPath:@"hunger"]; // Observer class of ‘me’ object - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // . . . } -
Why not KVO? // Some controller or else [dog addObserver:me
forKeyPath:@"hunger" options:NSKeyValueObservingOptionNew context:NULL]; // . . . [dog removeObserver:me forKeyPath:@"hunger"]; // Observer class of ‘me’ object - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // . . . } should remember pointer to observer object should remember key path should care of removing observer should implement observeValueForKeyPath... method in observer lots of if else statements because of keyPath -
Why not KVO? Unlike KVO, in ReactiveCocoa and FRP in
general you also can: -
Why not KVO? Unlike KVO, in ReactiveCocoa and FRP in
general you also can: combine operations together or derive state react on UI controls events and everything that changes over time operate with timers easily maintain networking operations operations can be chained sequently instead of nesting blocks -
Bindings Developing for Mac OS X reveals Bindings. allow programming
in IB replace boilerplate glue code -
Bindings Developing for Mac OS X reveals Bindings. allow programming
in IB replace boilerplate glue code This is real magic but . . . : no support for iOS development quite limited cannot be debugged -
Signals Signal – is a time varying value int a
= 0 a = 1 a = 2 . . . a = n -
Signals Signal – is a time varying value int a
= 0 a = 1 a = 2 . . . a = n 1 2 n . -
Events Signals send three different types of events to their
subscribers: next – provides a new value from the stream. -
Events Signals send three different types of events to their
subscribers: next – provides a new value from the stream. error – indicates that an error occurred before the signal could finish. -
Events Signals send three different types of events to their
subscribers: next – provides a new value from the stream. error – indicates that an error occurred before the signal could finish. completed – indicates that the signal finished successfully, and that no more values will be added to the stream. -
ReactiveCocoa Basics Stream Sequence Signal Stream (RACStream)– abstract representation of
series of values. Streams are Haskell monads. Signal (RACSignal) – push-driven stream, represents data that will be delivered in the future. Sequence (RACSequence) – pull-driven stream that behaves more like a collection (NSArray, etc) -
map filter flatten concat reduce fold merge combineLatest switchToLatest .
. . ReactiveCocoa Toolset -
[[RACObserve(self, username) filter:^(NSString *newName) { return [newName hasPrefix:@"a"]; }] subscribeNext:^(NSString
*newName) { NSLog(@"%@", newName); }]; Filtering object with value -
RAC(self.btnLogin, enabled) = [RACSignal combineLatest:@[ self.tfUsername.rac_textSignal, self.tfPassword.rac_textSignal ] reduce:^id(RACTuple *fields)
{ NSString *username = fields.first; NSString *password = fields.second; return @(username.length > 0 && password.length > 0); }]; Combining and reducing Signals -
RAC(self.btnLogin, enabled) = [RACSignal combineLatest:@[ self.tfUsername.rac_textSignal, self.tfPassword.rac_textSignal ] reduce:^id(RACTuple *fields)
{ NSString *username = fields.first; NSString *password = fields.second; return @(username.length > 0 && password.length > 0); }]; Combining and reducing Signals combineLate st: reduce: Bind rac_textSign al rac_textSign al -
RAC(self.lblTimeStamp, text) = [[[[RACSignal interval:1] startWith:[NSDate date]] map:^id(NSDate *value) {
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:(NSMinuteCalendarUnit| NSSecondCalendarUnit) fromDate:value]; return [NSString stringWithFormat:@"%d:%02d", dateComponents.minute, dateComponents.second]; }] deliverOn:[RACScheduler mainThreadScheduler] ]; Working With Timers And Schedulers -
Non-FRP Approach [client logInWithSuccess:^{ [client loadCachedMessagesWithSuccess:^(NSArray *messages) { [client fetchMessagesAfterMessage:messages.lastObject
success:^(NSArray *nextMsgs) { NSLog(@"Fetched all messages."); } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; Reducing chains of callbacks -
Non-FRP Approach [client logInWithSuccess:^{ [client loadCachedMessagesWithSuccess:^(NSArray *messages) { [client fetchMessagesAfterMessage:messages.lastObject
success:^(NSArray *nextMsgs) { NSLog(@"Fetched all messages."); } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; } failure:^(NSError *error) { [self presentError:error]; }]; Reducing chains of callbacks Using ReactiveCocoa [[[[client logIn] sequenceNext:^{ return [client loadCachedMessages]; }] flattenMap:^(NSArray *messages) { return [client fetchMessagesAfterMessage: messages.lastObject]; }] subscribeError:^(NSError *error) { [self presentError:error]; } completed:^{ NSLog(@"Fetched all messages."); }]; -
Using switchToLatest RACSubject *letters = [RACSubject subject]; RACSubject *numbers =
[RACSubject subject]; RACSubject *signalOfSignals = [RACSubject subject]; RACSignal *switched = [signalOfSignals switchToLatest]; // Outputs: A B 1 D [switched subscribeNext:^(NSString *x) { NSLog(@"%@", x); }]; [signalOfSignals sendNext:letters]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [signalOfSignals sendNext:numbers]; [letters sendNext:@"C"]; [numbers sendNext:@"1"]; [signalOfSignals sendNext:letters]; [numbers sendNext:@"2"]; [letters sendNext:@"D"]; -
Where next? You can find full documentation on GitHub repo
of this project: http:/ /github.com/ReactiveCocoa/ ReactiveCocoa Should take a look at Elm and meet some FRP theory http:/ /elm-lang.org/ Lookup brief material on Wikipedia or some blogs -
What else? FRP-based languages and frameworks: Elm ClojureScript C# –
Rx JavaScript – Flapjax, RxJS, Bacon.js, Java – RxJava Ruby – Frapuccino