Last active
December 25, 2015 15:49
-
-
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…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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