• Stars
    star
    107
  • Rank 322,445 (Top 7 %)
  • Language
    Objective-C
  • License
    Other
  • Created almost 11 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Futures, for Objective-C, that automatically collapse so it's nearly impossible to mix up the level of future nesting despite the lack of generics.

Collapsing Futures

This is a library implementing futures in Objective-C, featuring:

  • Types: TOCFuture to represent eventual results, TOCCancelToken to propagate cancellation notifications, TOCFutureSource to produce and control an eventual result, and TOCCancelTokenSource to produce and control a cancel token.
  • Automatic Collapsing: You never have to worry about forgetting to unwrap or flatten a doubly-eventual future. A [TOCFuture futureWithResult:[TOCFuture futureWithResult:@1]] is automatically a [TOCFuture futureWithResult:@1].
  • Sticky Main Thread: Callbacks registered from the main thread will get back on the main thread before executing. Makes UI work much easier.
  • Cancellable Operations: All asynchronous operations have variants that can be cancelled by cancelling the TOCCancelToken passed to the operation's until: or unless: parameter.
  • Immortality Detection: It is impossible to create new space leaks by consuming futures and tokens (but producers still have to be careful). If a reference cycle doesn't involve a token or future's source, it will be broken when the source is deallocated.
  • Documentation: Useful doc comments on every method and type, that don't just repeat the name, covering corner cases and in some cases basic usage hints. No 'getting started' guides yet, though.

Recent Changes

  • Version 1.
  • Deprecated "TwistedOakCollapsingFutures.h" for "CollapsingFutures.h".
  • Futures are now equatable (by current state then by will-end-up-in-same-state-with-same-value).

Installation

Method #1: CocoaPods

  1. In your Podfile, add pod 'TwistedOakCollapsingFutures', '~> 1.0'
  2. Run pod install from the project directory
  3. #import "CollapsingFutures.h" wherever you want to use futures, cancel tokens, or their category methods

Method #2: Manual

  1. Download one of the releases, or clone the repo
  2. Copy the source files from the src/ folder into your project
  3. Have ARC enabled
  4. #import "CollapsingFutures.h" wherever you want to use futures, cancel tokens, or their category methods

Usage

External Content

Using a Future

The following code is an example of how to make a TOCFuture do something. Use thenDo to make things happen when the future succeeds, and catchDo to make things happen when it fails (there's also finallyDo for cleanup):

#import "CollapsingFutures.h"

// ask for the address book, which is asynchronous because IOS may ask the user to allow it
TOCFuture *futureAddressBook = SomeUtilityClass.asyncGetAddressBook;

// if the user refuses access to the address book (or something else goes wrong), log the problem
[futureAddressBook catchDo:^(id error) {
    NSLog("There was an error (%@) getting the address book.", error);
}];

// if the user allowed access, use the address book
[futureAddressBook thenDo:^(id arcAddressBook) {
    ABAddressBookRef addressBook = (__bridge ABAddressBookRef)arcAddressBook;
    
    ... do stuff with addressBook ...
}];

Creating a Future

How does the asyncGetAddressBook method from the above example control the future it returns?

In the simple case, where the result is already known, you use TOCFuture futureWithResult: or TOCFuture futureWithFailure.

When the result is not known right away, the class TOCFutureSource is used. It has a future property that completes after the source's trySetResult or trySetFailure methods are called.

Here's how asyncGetAddressBook is implemented:

#import "CollapsingFutures.h"

+(TOCFuture *) asyncGetAddressBook {
    CFErrorRef creationError = nil;
    ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &creationError);
    
    // did we fail right away? Then return an already-failed future
    if (creationError != nil) {
        return [TOCFuture futureWithFailure:(__bridge_transfer id)creationError];
    }
    
    // we need to make an asynchronous call, so we'll use a future source
    // that way we can return its future right away and fill it in later
    TOCFutureSource *resultSource = [FutureSource new];
        
    id arcAddressBook = (__bridge_transfer id)addressBookRef; // retain the address book in ARC land
    ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef requestAccessError) {
        // time to fill in the future we returned
        if (granted) {
            [resultSource trySetResult:arcAddressBook];
        } else {
            [resultSource trySetFailure:(__bridge id)requestAccessError];
        }
    });
            
    return resultSource.future;
}

Chaining Futures

Just creating and using futures is useful, but not what makes them powerful. The true power is in transformative methods like then: and toc_thenAll that both consume and produce futures. They make wiring up complicated asynchronous sequences look easy:

#import "CollapsingFutures.h"

+(TOCFuture *) sumOfFutures:(NSArray*)arrayOfFuturesOfNumbers {
    // we want all of the values to be ready before we bother summing
    TOCFuture* futureOfArrayOfNumbers = arrayOfFuturesOfNumbers.toc_thenAll;
    
    // once the array of values is ready, add up its entries to get the sum
    TOCFuture* futureSum = [futureOfArrayOfNumbers then:^(NSArray* numbers) {
        double total = 0;
        for (NSNumber* number in numbers) {
            total += number.doubleValue;
        }
        return @(total);
    }];
    
    // this future will eventually contain the sum of the eventual numbers in the input array
    // if any of the evetnual numbers fails, this future will end up failing as well
    return futureSum;
}

The ability to setup transformations to occur once futures are ready allows you to write truly asynchronous code, that doesn't block precious threads, with very little boilerplate and intuitive propagation of failures.

Design Philosophy

  • No Blocking: There are no blocking operations included in the library, because blocking on a future is a great way to introduce deadlocks and UI hangs. Use then/finally/catch/whenCancelledDo instead.

  • No @catch-ing Errors: Raised errors are not caught, even inside handlers given to methods like then. In Objective-C, raising an error is generally considered to be a fatal problem. Catching them and packaging them into the future's failure could allow the program to continue despite its state being seriously corrupted.

  • No Garbage: Registered callbacks aren't just ignored when they stop mattering; they are actively removed and discarded. When a cancel token's source is deallocated before cancelling the token, the handlers are not kept around. When toc_finallyAll:unless: is cancelled, it does not forget about the callbacks it registered.

  • No Undefined Behavior: Corner cases should be accounted for in the implementation, specification, documentation, and tests of a method. Set a future to be its own result, directly or indirectly? It becomes immortal. Call whenCancelledDo:unless: on a cancelled token with a cancelled token? The callback doesn't run. Etc.

  • No Inheritance: A future source is not a future extended to be writable. A future source has a future that it controls. This is fundamental to ensuring consumers can't create memory leaks involving futures. Only producers can sustain those leaks, because as soon as the source is deallocated its future becomes immortal and all its callbacks get discarded.

Design Foibles

Bothing's perfect. This is a list of things that bother me, although they probably cause few or no problems in practice. I might fix them, but only between major versions because they require breaking changes.

  • Mutating Equality: Completed futures are equated by value, but incomplete futures are equated by reference. Convenient when you want to know if two completed futures are the same, but not so convenient if you put futures in a collection that uses hash codes for faster lookup (because the hash code changes!). If you insert incomplete futures into sets, or use them as dictionary keys, you may be unable to find them in the set after they've completed.
  • Two cancels: TOCCancelTokenSource has both tryCancel and cancel. The only difference is the returned value. Would be simpler to just have cancel.

API Breakdown

TOCFuture: An eventual result.

  • +futureWithResult:(id)resultValue: Returns a future that has already succeeded with the given value. If the value is a future, collapse occurs and its result/failure is used instead.
  • +futureWithFailure:(id)failureValue: Returns a future that has already failed with the given value. Variants for timeout and cancel failures work similarly.
  • +new: Returns an immortal future. (Not very useful.)
  • +futureFromOperation:(id (^)(void))operation dispatchedOnQueue:(dispatch_queue_t)queue: Dispatches an asynchronous operation, exposing the result as a future.
  • +futureFromOperation:(id(^)(void))operation invokedOnThread:(NSThread*)thread: Runs an asynchronous operation, exposing the result as a future.
  • +futureWithResult:(id)resultValue afterDelay:(NSTimeInterval)delayInSeconds: Returns a future the completes after a delay. An unless: variant allows the future to be cancelled and the timing stuff cleaned up.
  • +futureFromUntilOperation:withOperationTimeout:until:: Augments an until-style asynchronous operation with a timeout, returning the may-timeout future. The operation is cancelled if the timeout expires before completion. The operation is cancelled and/or its result cleaned up when the token is cancelled.
  • +futureFromUnlessOperation:withOperationTimeout:: Augments an unless-style asynchronous operation with a timeout, returning the may-timeout future. The operation is cancelled if the timeout expires before the operation completes. An unless variant allows the operation to also be cancelled if a token is cancelled before it completes.
  • cancelledOnCompletionToken: Returns a TOCCancelToken that becomes cancelled when the future has succeeded or failed.
  • state: Determines if the future is still able to be set (incomplete), failed, succeeded, flattening, or known to be immortal.
  • isIncomplete: Determines if the future is still able to be set, flattening, or known to be immortal.
  • hasResult: Determines if the future has succeeded with a result.
  • hasFailed: Determines if the future has failed.
  • hasFailedWithCancel: Determines if the future was cancelled, i.e. failed with a TOCCancelToken as its failure.
  • hasFailedWithTimeout: Determines if the future timed out, i.e. failed with a TOCTimeout as its failure.
  • forceGetResult: Returns the future's result, but raises an exception if the future didn't succeed with a result.
  • forceGetFailure: Returns the future's result, but raises an exception if the future didn't fail.
  • finally[Do]:block [unless:token]: Runs a callback when the future succeeds or fails. Passes the completed future into the block. The non-Do variants return a future that will eventually contain the result of evaluating the result-returning block.
  • then[Do]:block [unless:token]: Runs a callback when the future succeeds. Passes the future's result into the block. The non-Do variants return a future that will eventually contain the result of evaluating the result-returning block, or else the same failure as the receiving future.
  • catch[Do]:block [unless:token]: Runs a callback when the future fails. Passes the future's failure into the block. The non-Do variants return a future that will eventually contain the same result, or else the result of evaluating the result-returning block.
  • isEqualToFuture:(TOCFuture*)other: Determines if this future is in the same state and, if completed, has the same result/failure as the other future.
  • unless:(TOCCancelToken*)unless: Returns a future that will have the same result, unless the given token is cancelled first in which case it fails due to cancellation.

TOCFutureSource: Creates and controls a TOCFuture.

  • +new: Returns a new future source controlling a new future.
  • +futureSourceUntil:(TOCCancelToken*)untilCancelledToken: Returns a new future source controlling a new future, wired to automatically fail with cancellation if the given token is cancelled before the future is set.
  • future: Returns the future controlled by this source.
  • trySetResult:(id)result: Sets the controlled future to succeed with the given value. If the result is a future, collapse occurs. Returns false if the future was already set, whereas the force variant raises an exception.
  • trySetFailure:(id)result: Sets the controlled future to fail with the given value. Returns false if the future was already set, whereas the force variant raises an exception. Variants for cancellation and timeout work similarly.

TOCCancelToken: Notifies you when operations should be cancelled.

  • +cancelledToken: Returns an already cancelled token.
  • +immortalToken: Returns a token that will never be cancelled. Just a [TOCCancelToken new] token, but acts exactly like a nil cancel token.
  • state: Determines if the cancel token is cancelled, still cancellable, or known to be immortal.
  • isAlreadyCancelled: Determines if the cancel token is already cancelled.
  • canStillBeCancelled: Determines if the cancel token is not cancelled and not known to be immortal.
  • whenCancelledDo:(TOCCancelHandler)cancelHandler, whenCancelledDo:(TOCCancelHandler)cancelHandler unless:(TOCCancelToken*)unlessCancelledToken: Registers a void callback to run after the token is cancelled. Runs inline if already cancelled. The unless variant allows the callback to be removed if it has not run and is no longer needed (indicated by the other token being cancelled first).
  • +matchFirstToCancelBetween:(TOCCancelToken*)token1 and:(TOCCancelToken*)token2: Returns a token that is the minimum of two tokens. It is cancelled as soon as either of them is cancelled.
  • +matchLastToCancelBetween:(TOCCancelToken*)token1 and:(TOCCancelToken*)token2: Returns a token that is the maximum of two tokens. It is cancelled only when both of them is cancelled.

TOCCancelTokenSource: Creates and controls a cancel token.

  • +new: Returns a new cancel token source that controls a new cancel token.
  • +cancelTokenSourceUntil:(TOCCancelToken*)untilCancelledToken: Returns a cancel token source that controls a new cancel token, but wired to cancel automatically when the given token is cancelled.
  • token: Returns the cancel token controlled by the source.
  • cancel: Cancels the token controlled by the source. Does nothing if the token is already cancelled.
  • tryCancel: Cancels the token controlled by the source, returning false if it was already cancelled.

NSArray+: We are one. We are many. We are more.

  • toc_thenAll, toc_thenAllUnless:(TOCCancelToken*)unless: Converts from array-of-future to future-of-array. Takes an array of futures and returns a future that succeeds with an array of those futures' results. If any of the futures fails, the returned future fails. Example: @[[TOCFuture futureWithResult:@1], [TOCFuture futureWithResult:@2]].toc_thenAll evaluates to [TOCFuture futureWithResult:@[@1, @2]].
  • toc_finallyAll, toc_finallyAllUnless:(TOCCancelToken*)unless: Awaits the completion of many futures. Takes an array of futures and returns a future that completes with an array of the same futures, but only after they have all completed. Example: @[[TOCFuture futureWithResult:@1], [TOCFuture futureWithFailure:@2]].toc_finallyAll evaluates to [TOCFuture futureWithResult:@[[TOCFuture futureWithResult:@1], [TOCFuture futureWithFailure:@2]]].
  • toc_orderedByCompletion, toc_orderedByCompletionUnless:(TOCCancelToken*)unless: Returns an array with the "same" futures, except re-ordered so futures that will complete later will come later in the array. Example: @[[TOCFutureSource new].future, [TOCFuture futureWithResult:@1]].toc_orderedByCompletion returns @[[TOCFuture futureWithResult:@1], [TOCFutureSource new].future].
  • toc_raceForWinnerLastingUntil:(TOCCancelToken*)untilCancelledToken: Takes an array of TOCUntilOperation blocks. Each block is a cancellable asynchronous operation, returning a future and taking a cancel token that cancels the operations and/or cleans up the operation's result. The returned future completes with the result of the first operation to finish (or else all of their failures). The result of the returned future is cleaned up upon cancellation.

Development

How to Build:

  1. Get Source Code: Clone this git repository to your machine.

  2. Get Dependencies: Have CocoaPods installed. Run pod install from the project directory.

  3. Open Workspace: Open CollapsingFutures.xworkspace with XCode (not the project, the workspace). Run tests and confirm that they pass.

More Repositories

1

Quirk

A drag-and-drop quantum circuit simulator that runs in your browser. A toy for exploring and understanding small quantum circuits.
JavaScript
838
star
2

May

An option type for .Net
C#
32
star
3

Tinker

Warcraft 3 game hosting bot
Visual Basic .NET
24
star
4

PickleJar

A parser/packer combinator library for .net, where "Jars" (a serializer paired with a deserializer) are created by combining and augment primitive jars into complex jars. The resulting combinators are optimized and compiled at runtime.
C#
23
star
5

quantumpseudocode

An experimental python library for succinctly specifying quantum algorithms in an imperative pythonic style.
Python
16
star
6

python-chp-stabilizer-simulator

Simple python implementation of Scott Aaronson et al's CHP simulator.
Python
16
star
7

LinqToCollections

Implements types and extension methods for working with IReadOnlyList, IReadOnlyCollection, and IReadOnlyDictionary in a functional style.
C#
14
star
8

honeycomb_threshold

Code for estimating the threshold of the honeycomb code
Python
13
star
9

Methods

Interesting methods and types that aren't large enough to warrant their own repositories, but that I want to link to.
C#
11
star
10

Wc3PowerTowers

Various resources extracted from my Warcraft 3 map, Power Towers.
11
star
11

Quantum-Pseudo-Telepathy

Code demonstrating the quantum process used to consistently win a game that can only be won 8 times out of 9 classically.
C#
9
star
12

PaperImpl-2017-DirtyPeriodFinding

Example ProjectQ implementation of the constructions described in my preprint "Factoring with n+2 clean qubits and n-1 dirty qubits".
Python
9
star
13

IntervalReferences

Tracking nesting depth to allow efficient array slicing, with no memory leaks and logarithmic slicing costs.
C#
8
star
14

qbp_np_younes_test

Simulated implementation of a claimed NP=BQP algorithm, showing that it doesn't work.
Python
7
star
15

CliffordTableau

Represent Clifford operations using a table.
Python
6
star
16

UnionFind-.Net

A simple implementation of a disjoint set data structure in C#.
C#
6
star
17

quantum-karatsuba-2019

Reversible Karatsuba multiplication in O(n) space and O(n^lg(3)) operations.
TeX
6
star
18

Angle

A simple library for working with directions and rotations in 2d.
C#
5
star
19

Hand-Qubits

Qubits you can hold in your hands.
C++
5
star
20

Strilanc.github.io

Algorithmic Assertions - Craig Gidney's Computer Science Blog
HTML
5
star
21

PolyNumber

Representing numbers as the roots of polynomials with integer coefficients. More interesting than efficient or useful, especially considering the inability to represent sqrt(2) without also having -sqrt(2).
C#
5
star
22

honeycomb-boundaries

Python
4
star
23

NP-vs-Quantum-Simulation-Walters-Algorithm

Simulates the algorithm from the paper 'A linear time quantum algorithm for 3SAT', which is actually exponential time.
F#
4
star
24

efficient-quantum-factoring-2019

Latex and etc for the paper "How to factor 2048 bit numbers in 8 hours with 20 million noisy qubits"
TeX
4
star
25

Solomonoff-Mad-Scientist

For science!
C#
4
star
26

GameJam2013-AtTheCore

A game made in 48 hours at the 2013 halifax game jam.
C#
4
star
27

AnimatronTheTerrible

Creates and records visualizations. Warning: the code is not cleaned up or intended to be readable/usable by others, as it is made up of many layers of experimentation and hackery.
C#
4
star
28

matching-example

Minimum weight perfect matching in Euclidean space
JavaScript
3
star
29

Snitch

A surface code simulator.
JavaScript
3
star
30

slowmatch

Example of minimum weight embedded matching in python.
Python
3
star
31

Example-Custom-Virtualizing-Search-for-Windows-Store-Apps

An example implementation of UI virtualizing to make a fast search in windows store apps.
C#
3
star
32

Rational-Secret-Sharing-Master-s-Thesis

My master's thesis on rational secret sharing. Includes the thesis itself (latex and pdf), an implementation of the protocols described (C#), as well as the slides from my defense.
TeX
3
star
33

NotAMonad

A pedagogical repository, containing examples of types that are monads (i.e. have wrapping, transforming, flattening methods) and also types that aren't (at least, not in a natural or desirable way).
C#
3
star
34

AS3-Async-Tasks

.Net-style tasks for action script 3
ActionScript
2
star
35

ZigXag

A point-and-click ZX calculus graph editor with built-in simulation and circuit export tooling.
JavaScript
2
star
36

MpqLibrary

A library for reading MPQ files (targeted at wc3 maps)
Visual Basic
2
star
37

Quantum-Optics-Simulator

A hacky optical interferometry simulator based on quantum mechanics.
C#
2
star
38

stability-sims-2022

Code for "Stability Experiments: The Overlooked Dual of Memory Experiments"
Python
2
star
39

Toy-Hash-Break

Random bits of code I used when trying to find preimages for the outputs of a hash function included in a particular WarCraft 3 map.
C#
2
star
40

Java-Cancel-Tokens

An implementation of a cancel token, an object to conveniently register and invoke cleanup methods, in java.
2
star
41

Eve-Quantum-Clone-Computer

As you ask Eve to perform operations and measurements, she keeps track of what has and hasn't been ruled out about the current state. Sometimes she ends up knowing enough to make a near-perfekt clone.
JavaScript
2
star
42

UniversalTernaryGates

Code to find and check for universal ternary gates.
C#
1
star
43

Qubery

Controlling a simulated quantum computer with visually tracked origami checkerboard cubes.
Python
1
star
44

stim-paper

The latex for the paper describing Stim.
C++
1
star
45

Bell-Tester

An interactive JavaScript/html widget for trying classical and quantum strategies in the CHSH bell test game.
JavaScript
1
star
46

Schonhage-Strassen-Animation

Hacky code that animates an FFT-based multiplication on a user entered number.
JavaScript
1
star
47

SecureMultiPartyFogOfWar

An example of using secure multi party computation to compute fog of war in a peer to peer setting without ever sending private unit positions.
1
star
48

Croslyn

Creating and testing neat C# refactorings using the Roslyn CTP.
C#
1
star
49

linked-chain-erasure-survival

Demonstrating it's possible to correct erasure errors that form a chain link fence across a surface code patch.
Python
1
star
50

Python-Mish-Mash

A mish-mash of code I wrote in python that's interesting enough to keep.
Python
1
star
51

SurfaceCodeTurnTable

Experimental surface code simulator where the user activates/deactivates stabilizer configurations.
JavaScript
1
star
52

quantum-block-lookahead-adder

A lookahead adder that parallelizes over blocks of bits instead of over every bit.
TeX
1
star
53

heavy-hex-demo

Demo of benchmarking the heavy hex code with stim.
Python
1
star