Skip to content

Instantly share code, notes, and snippets.

@jonsterling
Last active December 25, 2015 15:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonsterling/7001562 to your computer and use it in GitHub Desktop.
Save jonsterling/7001562 to your computer and use it in GitHub Desktop.
Rethinking commands as Kleisli arrows with a bit more stuff. This show the basics of how to start with Kleisli arrows (of signals) which are interesting and useful in their own right. I often need to compose monadic pipelines `a -> Signal a`, and it's nice to be able to reify these rather than thread everything through (very pointy) flatmaps. Ne…
typedef RACSignal *(^RACSignalKleisliBlock)(id input);
@interface RACSignalKleisli : NSObject
- (id)initWithSignalBlock:(RACSignalKleisliBlock)block;
- (instancetype)compose:(RACSignalKleisli *)kleisli;
- (RACSignal *)execute:(id)input;
@end
@implementation RACSignalKleisli {
RACSignalBlock _signalBlock;
}
- (id)initWithSignalBlock:(RACSignalBlock)block {
if (self = [super init]) {
_signalBlock = [block copy];
}
return self;
}
- (instancetype)pullback:(RACSignalKleisli *)kleisli {
return [[self.class alloc] initWithSignalBlock:^RACSignal * (id input) {
return [[kleisli execute:input] flattenMap:^(id mappedInput) {
return [self execute:mappedInput];
}];
}];
}
- (RACSignal *)execute:(id)input {
return [_signalBlock(input) replay];
}
@end
@interface RACCommand : NSObject
- (id)initWithEnabled:(RACSignal *)enabled kleisli:(RACSignalKleisli *)kleisli;
@property (nonatomic, strong, readonly) RACSignal *executing;
@property (nonatomic, strong, readonly) RACSignal *enabled;
- (RACSignal *)execute:(id)input;
@end
@implementation RACCommand {
RACSignalKleisli *_decoratedKleisli;
}
- (id)initWithEnabled:(RACSignal *)enabled kleisli:(RACSignalKleisli *)kleisli {
if (self = [super init]) {
_enabled = [enabledSignal replayLast];
RACSignalKleisli *disable = [RACSignalKleisli kleisli:^(id input) {
return [[self.enabled take:1] flattenMap:^(NSNumber *enabled) {
if (enabled.boolValue) {
return [RACSignal return:input];
}
else {
NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil), RACUnderlyingCommandErrorKey: self }];
return [RACSignal error:error];
}
}];
}];
_decoratedKleisli = [kleisli pullback:disable];
}
return self;
}
- (RACSignal *)execute:(id)input {
return [_decoratedKleisli execute:input];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment