Concurrent Programming Library, Feedback Wanted

Hey All, I want to share something I’ve been working on and get some feedback. If you’re interested in concurrency 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 clattner “pyramid of doom”. Note that the HoneyBee form allows us to effortlessly parallelize the two data fetches.

Many more features are described at HoneyBee.link 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

What do you think? What’s working here? More importantly, what’s not working?
Thanks for your time. Looking forward to your feedback,

Alex

Terms of Service

Privacy Policy

Cookie Policy