Concurrency: Expressive, Easy and Safe.

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