Hey All, I want to share something I’ve been working on. I know this isn't strictly "swift-evolution" content, but I think it relates heavily to the ongoing conversation around concurrency features in the language (such as async/await). If you're interested in such topics would you mind taking a look?
HoneyBee.start { root in root.setErrorHandler(handleError) .chain(fetchNewMovieTitle) .branch { stem in stem.chain(fetchReviews) .chain(averageReviews) + stem.chain(fetchComments) .chain(countComments) } .setBlockPerformer(DispatchQueue.main) .chain(updateUI) } func handleError(_ error: Error) {} func fetchNewMovieTitle(completion: (String?, Error?) -> Void) {} func fetchReviews(for movieTitle: String, completion: (FailableResult<[String]>) -> Void) {} func averageReviews(_ reviews: [String]) throws -> Int { return reviews.count } func fetchComments(for movieTitle: String, completion: (([String]?, Error?) -> Void)?) {} func countComments(_ comments: [String]) -> Int { return comments.count } func updateUI(withAverageReview: Int, commentsCount: Int) {} (Yes that compiles. Much thanks to the Language Team.) HoneyBee makes concurrent programming expressive, easy and safe. The above recipe lexically matches the flow of execution. First `fetchNewMovieTitle` is invoked. Then `fetchReviews` and `fetchComments` are invoked in parallel each receiving the result of `fetchNewMovieTitle`. `averageReviews` is invoked after `fetchReviews` finishes and `countComments` is invoked after `fetchComments`. The results of `averageReviews` and `countComments` are combined and forwarded to `updateUI`, which is invoked on the main queue. HoneyBee.start(on: DispatchQueue.main) { root in root.setErrorHandler(handleError) .chain(fetchNewMovieTitle) .chain(fetchReviews) .map { elem in // parallel map elem.chain(\.count) // Keypath access } .filter { elem in // parallel filtering elem.chain(isNonTrivial) } .reduce { pair in // parallel "pyramid" reduce pair.chain(+) // operator access } .chain(updateUI) } func handleError(_ error: Error) {} func fetchNewMovieTitle(completion: (String?, Error?) -> Void) {} func fetchReviews(for movieTitle: String, completion: (FailableResult<[String]>) -> Void) {} func isNonTrivial(_ int: Int, completion: (Bool) -> Void) {} func updateUI(withTotalWordsInNonTrivialReviews: Int) {} The above recipe demonstrates use of parallel map, filter and reduce. The entire recipe is run on the main queue. struct Image {} func processImageData(completionBlock: @escaping (Image?, Error?) -> Void) { func loadWebResource(named name: String, completion: (Data?, Error?) -> Void) {} func decodeImage(dataProfile: Data, image: Data) throws -> Image { return Image() } func dewarpAndCleanupImage(_ image: Image, completion: (Image?, Error?) -> Void) {} HoneyBee.start { root in root.setErrorHandler { completionBlock(nil, $0) } .branch { stem -> Link<(Data,Data)> in stem.chain(loadWebResource =<< "dataprofile.txt") + stem.chain(loadWebResource =<< "imagedata.dat") } .chain(decodeImage) .chain(dewarpAndCleanupImage) .chain{ completionBlock($0, nil) } } } The above recipe is a translation of the now famous clatner "pyramid of doom" <https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782>. Note that the HoneyBee form allows us to effortlessly parallelize the two data fetches. HoneyBee has many more features including: * Total of 34 chain signatures * Limit to globally control accesses to a resource * Retry to re-attempt subchains that are known to have transient failures * Helpful diagnostics when things go wrong See the full documentation at HoneyBee.link Thanks for your time. Looking forward to your feedback, Alex
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution