The Full Air API Reference

This pagee contains the full API documentation for Air. This includes both the documentation for public and private objects.

Index

API Documents

Air.airout — Constant
airout

The airout object is a AirOut object that wraps the stdout object.

See also: AirOut

source
Air.AbstractPArray — Type
AbstractPArray{T,N}

The Abstract persistent array type represents any Array-like type that is persistent. The Air library provides the reified type PArray{T,N}.

See also: PArray, PVector.

source
Air.Actor — Type
Actor{T}

An actor is an object that represents a worker-thread to which tasks can be scheduled. Any scheduled function is guaranteed to be evaluated at some point in the future in some other thread, and the return value of that function will become the new value held by the actor. Each function, when it is run, is passed the actor's value as one of its argument.

Like with Refs, you can access an actor's current value using getindex, e.g., actor[]. Within a transaction, this will be guaranteed to remain fixed for the duration of the transaction; outside of a transaction, this may change at arbitrary times.

Actors may be given two hooks, either at construction (with the postprocess and errorhandler keywords) or with setpostprocess! and seterrorhandler!.

A post-processing function is called immediately after each scheduled function has run, and is given the actor's value before the call as its first argument and the scheduled function's return value as its second. The value it returns, rather than the scheduled function's own return value, is stored in the actor (and converted to T); the post-processing function is not called if the scheduled function throws. This is useful for actors whose job is, for example, to serialize output to a log file written to across many threads: if the actor's value is the IO stream, a scheduled function such as io -> println(io, "message") returns nothing, and a post-processor of (io, _) -> io puts the stream back in the actor so that subsequent send calls can keep writing to it.

An error handler is called when a scheduled function—or the post-processing function—throws. It is given (1) the actor, (2) the actor's value when the failing function was scheduled, and (3) the exception. The actor still enters its error state: the handler is for observing or logging the failure, not for recovering the actor, and its return value is ignored. An exception thrown by the error handler itself is not caught and ends the actor's processing task.

Both hooks run on the actor's own task. Setting a hook affects the next function dequeued, never one that is already running. When an error has occurred, any attempt to schedule a function to the actor or to read from the actor will cause an exception to be raised. The error may be examined using geterror(actor) and cleared using reset(actor).

All fields in an actor object should be considered strictly private. These fields are likely to change between releases, and changing the values will break your code.

See also: tx, @tx, send, geterror, reset.

Examples

DocTestSetup = quote
    using Air
end
julia> a = Actor{Symbol}(:start_sym)
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> send(a) do val; sleep(0.1); Symbol("new_$(val)") end
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> sleep(1); a[]
:new_start_sym
source
Air.ActorException — Type
ActorException{T}(error, value, fn, queue)

An ActorException object is thrown whenever one attempts to obtain the value of or send a function to an actor that is in an error state. An error state occurs when an exception is raised while an actor is processing a sent function or its post-processing function. In such a case the geterror and reset functions may be used. geterror yields an ActorException object in which the exception that was raised is stored as error, the actor's value when the failing function was scheduled is stored as value, the scheduled function itself is fn, and queue is the tuple of messages that were still pending when the error occurred.

See also: Actor, geterror, reset, send

source
Air.ActorMsg — Type
ActorMsg

A message, queued for an Actor via the send() function.

The ActorMsg struct is considered part of Air's private/internal implementation details.

source
Air.ActorTxData — Type
ActorTxData{T}

The data tracked for an Actor{T} during a transaction by the transaction's Transaction struct.

The ActorTxData struct is considered part of the internal/private code of Air and generally should not be used outside of the Air library.

source
Air.AirArrayStyle — Type
AirArrayStyle{N}

The broadcast style of the persistent arrays and their transients. Declaring one is what keeps a broadcast over a PArray a PArray, and a broadcast over a TArray a TArray: without it the operation falls back to DefaultArrayStyle and produces a mutable Array.

This type is part of Air's internal/private implementation details.

source
Air.AirOut — Type
AirOut

The AirOut type is a derivative of the IO class and is intended for handling output to stdout across multiple threads in a thread-safe way. It is similar TxIO except that it only writes to stdout and is only really intended for use with the airout object.

See also: Actor, airout, TxIO

source
Air.Cancelled — Type
Cancelled(token, reason)

An exception thrown by a blocking Air primitive when the CancelToken it was given has been cancelled. token is the token and reason is the reason given to cancel!, which may be nothing.

See also: CancelToken, cancel!, checkcancel

source
Air.Delay — Type
Delay{T}

Delay objects can be used to lazily calculate a single value the first time it is requested. They act like Refs in that you access a delay d via d[]. Delay objects are thread-safe and are functionally immutable.

See also: @delay, LazyDict

Examples

DocTestSetup = quote
    using Air
end
julia> # Create a Delay with a long run time.
       d = Delay{Int64}(() -> (println("Running."); sleep(2); 10))
Delay{Int64}(<...>)

julia> # Start a few threads, each of which attempt to read it. The function
       # will only run once.
       for th in [(Threads.@spawn d[]) for _ in 1:5]; wait(th) end
Running.

julia> # Ensure that it produced the correct value and doesn't run again.
       d[]
10
source
Air.LazyDict — Type
LazyDict{K,V}

A dictionay type equivalent to PDict{K,V} in every way except that for any value in the dict that is a Delay object, the dictionary hides the delay and always returns the delay's value. This allows any value that has not yet been requested to be lazily unevaluated.

See also: PDict, Base.Dict, LazyIdDict, Delay, @delay.

Examples

DocTestSetup = quote
    using Air
end
julia> LazyDict()
LazyDict{Any,Any}()
julia> d = LazyDict(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:b], d[:c])
(1, 2, 12.8)
julia> d = LazyDict{Symbol,Float64}(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:c])
(1.0, 12.8)

julia> d2 = push(d, :d => Delay{Real}(() -> (println("Running..."); 0.5))); haskey(d2, :d)
true

julia> d2[:d]
Running...
0.5

julia> d2[:d]
0.5
source
Air.LazyIdDict — Type
LazyIdDict{K,V}

A dictionay type equivalent to PIdDict{K,V} in every way except that for any value in the dict that is a Delay object, the dictionary hides the delay and always returns the delay's value. This allows any value that has not yet been requested to be lazily unevaluated.

See also: PIdDict, Base.IdDict, LazyDict, Delay, @delay.

Examples

DocTestSetup = quote
    using Air
end
julia> LazyIdDict()
LazyIdDict{Any,Any}()
julia> d = LazyIdDict(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:b], d[:c])
(1, 2, 12.8)
julia> d = LazyIdDict{Symbol,Float64}(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:c])
(1.0, 12.8)

julia> d2 = push(d, :d => Delay{Real}(() -> (println("Running..."); 0.5)));

julia> haskey(d2, :d)
true

julia> d2[:d]
Running...
0.5

julia> d2[:d]
0.5
source
Air.PArray — Type
PArray{T,N}

The PArray type is a persistent/immutable corrolary to the Array{T,N} type. Like Array, PArray can store n-dimensional non-ragged arrays. However, unlike Arrays, PArrays can create duplicates of themselves with finite edits in log-time.

PArrays have a similar interface as Arrays, but instead of the functions push!, pop!, and setindex!, PArrays use push, pop, and setindex. PArrays also have efficient implementations of pushfirst and popfirst.

Examples

DocTestSetup = quote
    using Air
end
julia> PArray()
0-element PArray{Any,1}
julia> u = PArray{Int,1}([1,2])
2-element PArray{Int64,1}:
 1
 2

julia> push(u, 3)
3-element PArray{Int64,1}:
 1
 2
 3
julia> PArray{Symbol,2}(:abc, (2,3))
2×3 PArray{Symbol,2}:
 :abc  :abc  :abc
 :abc  :abc  :abc
source
Air.PDict — Type
PDict{K,V}

A dictionay type roughly equivalent to Dict{K,V} but that stores data using a persistent hash array mapped trie system that allows for efficient persistent operations. These operations are represented by non-mutating versions of the standard dictionary functions, such as setindex in place of setindex!, push in place of push!, and pop in place of pop!. These operations are performed in O(log n) time, and minimal data duplication is performed in update operations.

See also: PIdDict, Dict, push, pop, setindex, delete, insert, getpair.

Examples

DocTestSetup = quote
    using Air
end
julia> PDict()
PDict{Any,Any}()
julia> d = PDict(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:b], d[:c])
(1, 2, 12.8)
julia> d = PDict{Symbol,Float64}(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:c])
(1.0, 12.8)

julia> :b in keys(d)
true

julia> d[:c]
12.8

julia> d2 = push(d, :d => 0.1);

julia> (length(d2), d2[:d])
(4, 0.1)
source
Air.PHeap — Type
PHeap{T,W,F}

A PHeap object is a priority queue to which objects can be pushed and given a particular weight. PHeaps can then be iterated in weighted order, popped in weighted order, and the first element may be obtained in weighted order. Additionally, a random sample may be drawn from a heap, which uses the distribution implied by the relative weights of the items in the heap.

PHeap objects may be initialized with a single argument of type F <: Function, in which case this function is used to compare the weights for ordering. By default this is > such that high weights are retrieved first by the first() function and removed first by the pop() function. Note, however, that the relative weights are still used by the rand function regardless of the ordering function, so negative values cannot be used.

See also: PWSet, PWDict, pop, Base.first.

source
Air.PIdDict — Type
PIdDict{K,V}

A dictionay type roughly equivalent to IdDict{K,V} but that stores data using a persistent hash array mapped trie system that allows for efficient persistent operations. These operations are represented by non-mutating versions of the standard dictionary functions, such as setindex in place of setindex!, push in place of push!, and pop in place of pop!. These operations are performed in O(log n) time, and minimal data duplication is performed in update operations.

See also: PDict, Base.IdDict, push, pop, setindex, delete, insert, getpair.

Examples

DocTestSetup = quote
    using Air
end
julia> PIdDict()
PIdDict{Any,Any}()
julia> d = PIdDict(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:b], d[:c])
(1, 2, 12.8)
julia> d = PIdDict{Symbol,Float64}(:a => 1, :b => 2, :c => 12.8);

julia> (d[:a], d[:c])
(1.0, 12.8)

julia> :b in keys(d)
true

julia> d[:c]
12.8

julia> d2 = push(d, :d => 0.1);

julia> (length(d2), d2[:d])
(4, 0.1)
source
Air.PIdLinearDict — Type
PIdLinearDict{K,V}

An identity-based persistent dict type that uses a simple array representation internally; accordingly PIdLinearDict does not peform persistent operations such as setindex, push, and delete efficiently. It is intended to be used by Air internally as a sub-dict that stores items whose hash values are identical.

See also: PLinearDict, PIdDict.

source
Air.PIdLinearSet — Type
PIdLinearSet{T}

A persistent IdSet type that uses a simple array representation internally; accordingly PIdLinearSet does not peform persistent operations such as setindex, push, and delete efficiently. It is intended to be used by Air internally as a set that stores items whose hash values are identical.

See also: PLinearSet, PSet.

source
Air.PIdSet — Type
PIdSet{T}

A persistent identity-based set type that uses an array hash-mapped trie structure to allow for efficient updating of the set using the persistent operations defined by Air such as push, and delete.

See also: Set, PIdSet.

Examples

DocTestSetup = quote
    using Air
end
julia> PIdSet()
PIdSet{Any}()

julia> :a in PIdSet([:a, :b, :c])
true

julia> :d in PIdSet([:a, :b, :c])
false

julia> :d in push(PIdSet(), :d)
true
source
Air.PLinearDict — Type
PLinearDict{K,V}

A persistent dict type that uses a simple array representation internally; accordingly PLinearDict does not peform persistent operations such as setindex, push, and delete efficiently. It is intended to be used by Air internally as a sub-dict that stores items whose hash values are identical.

See also: PIdLinearDict, PDict.

source
Air.PLinearSet — Type
PLinearSet{T}

A persistent set type that uses a simple array representation internally; accordingly PLinearSet does not peform persistent operations such as setindex, push, and delete efficiently. It is intended to be used by Air internally as a set that stores items whose hash values are identical.

See also: PIdLinearSet, PSet.

source
Air.PMatrix — Type
PMatrix{T}

An alias for PArray{T,2}, representing a persistent matrix.

source
Air.PSet — Type
PSet{T}

A persistent set type that uses an array hash-mapped trie structure to allow for efficient updating of the set using the persistent operations defined by Air such as setindex, push, and delete.

See also: Set, PIdSet.

Examples

DocTestSetup = quote
    using Air
end
julia> PSet()
PSet{Any}()

julia> :a in PSet([:a, :b, :c])
true

julia> :d in PSet([:a, :b, :c])
false

julia> :d in push(PSet(), :d)
true
source
Air.PTree — Type
PTree{T}

PTree is a persistent tree/array hybrid that maps arbitrary unsigned integers (type HASH_T) to values. It can efficiently be used for arrays or for hash-maps, and supports efficient lookup, association, and dissociation.

All fields of a PTree should be considered strictly private, as modification of the fields may result in a kernel crash. The properties of a PTree are both immutable and safe to inpect.

source
Air.PVector — Type
PVector{T}

An alias for PArray{T,1}, representing a persistent vector.

source
Air.PWDict — Type
PWDict{K,V}

A persistent dictionary with weighted pairs. As such, a PWDict supports the typical operations of a PDict as well as the following:

  • The first function yields the key-value pair with the highest weight.
  • The pop function yields a copy of the dictionary without the pair that has the highest weight.
  • Iteration occurs in the order of greatest to least weight.
  • The weights can be changed with the getweight and setweight functions; setweight yields a duplicate dictionary with updated weights.

See also: PDict, getweight, setweight.

Examples

DocTestSetup = quote
    using Air
end
julia> PWDict{Symbol,Int}(:a => (1,0.1), :b => (2, 0.2), :c => (3, 0.3))
PWDict{Symbol,Int64,Float64} with 3 entries:
  :c => 3
  :b => 2
  :a => 1
source
Air.PWIdDict — Type
PWIdDict{K,V}

A persistent dictionary with weighted pairs. As such, a PWIdDict supports the typical operations of a PIdDict as well as the following:

  • The first function yields the key-value pair with the highest weight.
  • The pop function yields a copy of the dictionary without the pair that has the highest weight.
  • Iteration occurs in the order of greatest to least weight.
  • The weights can be changed with the getweight and setweight functions; setweight yields a duplicate dictionary with updated weights.

See also: PIdDict, PWDict, getweight, setweight.

Examples

DocTestSetup = quote
    using Air
end
julia> PWIdDict{Symbol,Int}(:a => (1,0.1), :b => (2, 0.2), :c => (3, 0.3))
PWIdDict{Symbol,Int64,Float64} with 3 entries:
  :c => 3
  :b => 2
  :a => 1
source
Air.PWIdSet — Type
PWIdSet{T}

A persistent set with weighted elements. As such, a PWIdSet supports the typical operations of a PIdSet as well as the following:

  • The first function yields the element with the highest weight.
  • The pop function yields a copy of the set without the element that has the highest weight.
  • Iteration occurs in the order of greatest to least weight.
  • The weights can be changed with the getweight and setweight functions; setweight yields a duplicate dictionary with updated weights.

See also: PIdSet, PWSet, getweight, setweight.

Examples

DocTestSetup = quote
    using Air
end
julia> PWIdSet{Symbol}(:a => 0.1, :b => 0.2, :c => 0.3)
PWIdSet{Symbol,Float64} with 3 elements:
  :c
  :b
  :a
source
Air.PWSet — Type
PWSet{K,V}

A persistent set with weighted elements. As such, a PWSet supports the typical operations of a PSet as well as the following:

  • The first function yields the element with the highest weight.
  • The pop function yields a copy of the set without the element that has the highest weight.
  • Iteration occurs in the order of greatest to least weight.
  • The weights can be changed with the getweight and setweight functions; setweight yields a duplicate dictionary with updated weights.

See also: PSet, getweight, setweight.

Examples

DocTestSetup = quote
    using Air
end
julia> PWSet{Symbol}(:a => 0.1, :b => 0.2, :c => 0.3)
PWSet{Symbol,Float64} with 3 elements:
  :c
  :b
  :a
source
Air.Promise — Type
Promise{T}
Promise{T}(; ondelivery = nothing, onfailure = nothing)
Promise(; ondelivery = nothing, onfailure = nothing)

Promise objects represent placeholders for values that are produced at some later point, typically by another task. A promise is settled exactly once: it is either delivered with a value by put!, or failed with an error payload by fail!. Until then it is pending. take (or p[] for promise p) yields the delivered value, throws PromiseFailed for a failed promise, and suspends the calling task until the promise is settled.

The ondelivery keyword installs a callback that is called with the delivered value, exactly once; onfailure installs one that is called with the failure payload. The callbacks are fixed when the promise is constructed and cannot be changed later. Their return values are ignored, so they are notifications rather than transformations, and an exception either callback throws is not caught. A callback is called in the latest world, so a long-lived task may settle a promise whose callback was defined after that task was created. Outside a transaction a callback runs in the task that settled the promise, after the settlement has been made; inside a transaction it runs in the finalize phase, before the transaction attempts to commit.

take also accepts a timeout and a cancellation token. A Promise compares by identity — two promises are equal only when they are the same object — and its hash never waits for a settlement, so a pending promise may be used as a dictionary key.

A Promise participates in transactions: a put! or fail! inside a transaction is staged and performed only if the whole transaction commits, and take, isready, isfailed and geterror inside that transaction see the staged settlement. See the "Promises and transactions" section of the manual for the details.

Examples

DocTestSetup = quote
    using Air
end
julia> p = Promise{Symbol}()
Promise{Symbol}(<...>)

julia> isready(p)
false

julia> put!(p, :value)
:value

julia> isready(p)
true

julia> take(p)
:value

julia> p[]
:value
source
Air.PromiseTxData — Type
PromiseTxData{T}

Holds a settlement staged for a Promise by the current transaction: a delivery in val or a failure in err, exactly one of which is non-nothing. Both are stored as Some wrappers, already converted, so the commit can assign the staged slot directly.

This struct is part of the internal/private code of Air and generally should not be used outside of the Air library.

source
Air.ReaderTimeout — Type
ReaderTimeout(timeout)

An exception thrown by a blocking read when the given timeout expired: by take when the promise was still pending, and by take or put! when a Stream was still empty or full. The timeout, in seconds, is stored as timeout.

See also: Promise, Stream, take

source
Air.ReentrantRef — Type
ReentrantRef{T}

A ReentrantRef is a type of ref object that is thread-safe. There are a few strategies for this, each of which are encoded in a different object type that inheits from ReentrantRef. These types are as follows.

Var: Var objects act like Ref objects except that changes to them are exclusively task-local and must be performed in specific scoped expressions. However, the state of all Var objects for the current task can also be saved and restored at a later point.

Actor: Actor objects obey the actor pattern; you can call send(fn, actor, args...) where fn is a function that is, in another thread at some point, called as fn(actor[], args...). The new value of the actor after running fn is the return value of the call.

Volatile: Volatile objects are Refs that can be changed by any thread but that must be changed only within a synchronized transaction that ensures that all reads and writes of volatiles, as well as reads from and sends to actors, are atomic: either they all happen successfully, or none of them do.

source
Air.Stream — Type
Stream{T}(; maxsize = nothing)
Stream(; maxsize = nothing)
Stream{T}(items::AbstractVector; maxsize = nothing)
Stream(items::AbstractVector; maxsize = nothing)

A Stream{T} is a FIFO of values of type T that may be written and read repeatedly, unlike the one-shot Promise. put! appends a value and take removes the oldest one. Both block when they cannot proceed — a read when the stream is empty, a write when it is full — and both accept the timeout and token keywords of take(::Promise).

maxsize bounds the stream: with maxsize = 3, three values may be waiting, and a fourth put! waits for a take. The default, nothing, leaves the stream unbounded, so put! never waits. Stream() is a Stream{Any}.

A stream may also start out holding values, given as any AbstractVector: the new stream starts with them queued, oldest first, and copies them, so the vector may be reused afterwards. The elements are converted to T as put! converts them, and a maxsize smaller than the number of items is an ArgumentError. Stream(items) is a Stream{eltype(items)}. This restores a snapshot, which is the way to bring back a serialized stream; the restored stream is open, so close it afterwards if the original was closed.

Outside a transaction, put! and take happen immediately, under the stream's lock. Inside one they are staged, and happen only if the transaction commits; the transaction that staged them sees its own writes and reads, and a concurrent change to the stream invalidates the transaction, which retries. Blocking inside a transaction is allowed, but waiting for a change that only the transaction's own body could make is a deadlock; a stream created in the current transaction therefore raises StreamDeadlock instead of blocking. See the Streams page for the full rules.

close ends the stream: a later put! throws StreamClosed, and a take drains the values that remain and then throws it as well. Streams compare by identity, and isopen, isempty, length and isready inspect one without blocking.

See also: take, StreamClosed, StreamDeadlock, maxsize

Examples

DocTestSetup = quote
    using Air
end
julia> s = Stream{Int}();

julia> put!(s, 1); put!(s, 2);

julia> take(s)
1

julia> take(s)
2

julia> isempty(s)
true

A bounded stream makes put! wait, and timeout bounds that wait:

julia> s = Stream{Int}(maxsize = 1);

julia> put!(s, 1);

julia> try
           put!(s, 2; timeout = 0.01)
       catch e
           sprint(showerror, e)
       end
"reader timed out after 0.01 seconds"

julia> take(s)
1

A stream can be created holding values, which is how a snapshot is restored:

julia> s = Stream{Int}([4, 5]; maxsize = 2);

julia> snapshot(s)
2-element Vector{Int64}:
 4
 5

julia> take(s)
4

close lets a reader drain what is left and then stop:

julia> s = Stream{Int}();

julia> put!(s, 7); close(s);

julia> take(s)
7

julia> try
           take(s)
       catch e
           e
       end
StreamClosed()
source
Air.StreamClosed — Type
StreamClosed()

An exception thrown when a closed Stream is written to, or read from once it is empty. A closed stream can still be drained by take until no items remain.

See also: Stream, close

source
Air.StreamDeadlock — Type
StreamDeadlock()

An exception thrown when an operation would block on a Stream that was created in the current transaction. No other task can have a reference to such a stream until the transaction commits, so waiting for one is a guaranteed deadlock; the stream itself is left unchanged.

See also: Stream, tx

source
Air.StreamTxData — Type
StreamTxData{T}

A transaction's staged view of a Stream: the version it first observed, how many live items it has taken, its staged puts, a staged close, and whether it created the stream. This is part of Air's private/internal implementation details.

source
Air.TArray — Type
TArray{T,N}

A mutable counterpart of PArray{T,N}, for batch updates. See the file comment for the contract; use transient to make one and persistent! to get a PArray back.

A TArray is an AbstractArray with the interface of an Array — it indexes, iterates, broadcasts, and assigns in place — but it is deliberately not an AbstractPArray, which is the hierarchy for persistent collections, since a method dispatching on that could build a persistent collection out of nodes the transient owns. Operations over a transient return TArrays. The operations that change the length are push! and pop!, on TArray{T,1}; for other shapes, setindex! changes entries without changing the shape.

source
Air.TDict — Type
TDict{K,V}

A mutable counterpart of PDict{K,V}, for batch updates: make many updates through it, then take the persistent result with persistent!. See transient and the transient.jl file comment for the contract; TIdDict is the identity-keyed counterpart of PIdDict.

When a transient is worth it

A batch update costs N + 1 allocations fewer through a TDict than through a PDict for every node on the path from the root that the transient has already claimed, but it also pays for making the transient and for the walk persistent! does to clear the ownership flags. So the gain is proportional to the batch and constant in the dictionary, and small batches lose:

entries in the dictionaryentries updatedPDictTDict
1000119 allocs24 allocs0.79x
1000101811840.98x
100100161212761.26x
1000100018451136731.35x
10000100019948156651.27x

The crossover is around ten updates: below it a transient costs more than it saves (about 20% more allocations for a single update), and above it the gain grows slowly to roughly a third fewer allocations by a few hundred updates. The time gain is smaller than the allocation gain — about 1.1x at the sizes above — because the transient does a little more work per update.

Note that this is the overwrite case, which is what a batch update usually means. Two things bound the gain: a collision bucket has no ownership flag, so bucket updates are copied as before (a bucket is usually touched once per batch anyway), and the ceiling is the tree path, which is most but not all of an update's allocations. For appending to a TArray the gain is much larger (2.9x fewer allocations), because consecutive appends claim the same twig.

source
Air.TIdDict — Type
TIdDict{K,V}

A mutable counterpart of PIdDict{K,V}, otherwise as TDict. Keys are compared by identity, as in IdDict.

source
Air.TIdSet — Type
TIdSet{T}

A mutable counterpart of PIdSet{T}, otherwise as TSet. Elements are compared by identity, as in IdSet.

source
Air.TSet — Type
TSet{T}

A mutable counterpart of PSet{T}, for batch updates: make many updates through it, then take the persistent result with persistent!. See transient and the transient.jl file comment for the contract; TIdSet is the identity-keyed counterpart of PIdSet.

A set is a dictionary with no values, so the shape of the trade-off is the same as for TDict — the gain is proportional to the batch and constant in the set — but the numbers are not, so they were measured separately. Adding elements, which is the analogous batch:

set entriesaddedPSetTSet
1000120 allocs25 allocs0.80x
1000101912140.89x
1000100190117721.07x
1000100019331168521.15x

A batch of a few additions therefore still costs more through a transient, and the gain reaches only about 15% by a thousand. Pushing an element that is already present changes nothing and costs the same either way (measured at 1.0x), so a batch of no-op updates neither helps nor hurts.

source
Air.Transaction — Type
Transaction

A transaction object keeps track of what is going on during a particular transaction. These are generally low-level objects that shouldn't be touched directly.

Transactions have the following properties:

  • state is one of :running, :finalizing, :locking, or :committing;
  • rvolatiles is the set of all Volatile objects that have been read during the transaction;
  • wvolatiles is the set of all Volatile objects that have been changed; and
  • actors is the set of all Actor objects to which messages have been sent during the transaction.
source
Air.TransactionalRef — Type
TransactionalRef{T}

A TransactionalRef is a reentrant reference that additional participates in transactions. Transactional refs include Actors and Volatiles.

source
Air.TxIO — Type
TxIO

The TxIO type is a derivative of the IO class and is intended for handling output to streams across multiple threads in a thread-safe way. TxIO objects can be constructed from IO objects and can be printed/written to like normal IO objects. However, all output is performed in a separate asynchronous thread, and writes that occur during a transaction always occur only if the transaction succeeds.

See also: Actor, airout

source
Air.TxRetryException — Type
TxRetryException

A TxRetryException is throws when a transaction needs to be retried but an error hasn't necessarily been generated otherwise. This can be thrown during a transaction to restart the transaction; though doing this can easily lead to infinite loops.

source
Air.Var — Type
Var{T}

A Var object represents a task-local piece of data with a default value. You can access a Var with the getindex function (var[]) and you can set it with the setindex! function (var[] = newval). However, the new assignment will always be task-local only. Because of this, Vars are safe to access and update in a multi-threaded program.

All fields of a Var should be considered strictly private.

See also: @var, ReentrantRef, vars, withvars, wrapsetvars, wrapwithvars, Threads.current_task, Threads.task_local_storage.

Examples

DocTestSetup = quote
    using Air
end
julia> v = Var{Symbol}(:start_sym)
Var{Symbol}(@JIm7aUS2sOl: :start_sym; init=:start_sym)

julia> v[]
:start_sym

julia> withvars(v => :new_sym) do; v[] end
:new_sym

julia> withvars(v => :new_sym) do; fetch(Threads.@spawn v[]) end
:start_sym

julia> v[]
:start_sym
source
Air.VarsDict — Type
VarsDict

The type of a dictionary of Var bindings, as returned by the function vars() and as is required by the function setvars().

source
Air.Volatile — Type
Volatile{T}(value)
Volatile{T}(value, filter_fn)
Volatile{T}(value, filter_fn, finalize_fn)

Volatile objects are Ref objects that must be used in conjunction with transaction blocks (see @tx and tx). The value of a Volatile can be accessed at any time, and there is no particular guarantee that a Volatile's value won't be changed by another thread outside of a transaction, thus reading them outside of a transaction can potentially create race conditions. Critically, Volatile objects can only be set inside of a transaction, and, within a transaction, a Volatile's value is guaranteed to be constant. In other words, Volatile objects are Ref objects that behave as if they are locked for the executing thread whenever that thread is inside a transaction block.

The arguments filter_fn and finnalize_fn are functions for making sure that the value of a volatile conforms to some standard. The two functions are similar but are intended for slightly different use cases:

  • filter_fn is called every time the value of a volatile is set. The value saved in the volatile is filter_fn(value) instead of value itself. Values are only filtered when the volatile is set directly (vol[] = value)—the filter is not rerun when the filter_fn is changed (via setfilter!) nor when the finalize_fn returns a value.
  • finalize_fn is called with the volatile's value immediately after the body of the transaction has completed, but before a commit is attempted. The finalize_fn function acts like the filter_fn in that finalize_fn(value) replaces value in the volatile.

Both filter_fn and finalize_fn can throw exceptions, which will abort the running transaction.

For a Volatile v, one may set v's stored value via v[] = value. This must be done inside a transaction (via tx or @tx). To change the filter or the finalize functions, use the setfilter! and setfinalize! functions, both of which also must be run in transactions as well.

See also: tx, @tx, setfilter!, setfinalize!.

Examples

DocTestSetup = quote
    using Air
end
julia> v = Volatile{Symbol}(:startval)
Volatile{Symbol}(@JIm7aUS2sOl: :startval)

julia> @tx v[] = :newval
:newval

julia> v[]
:newval
source
Air.Waiter — Type
Waiter

A one-task wake-up channel used by Air's blocking primitives. A waiter has its own lock and condition, and two flags: done (it was woken) and timedout (its timer expired). Waiting tasks park on the condition, and every path that should wake them — a promise settlement, a cancellation, or a timeout — calls _wake! or _timeout!, which take the waiter's lock and notify.

A waiter is created by the blocking call, so it has exactly one waiter task.

This struct is part of Air's private/internal implementation details.

source
Air._air_reshape — Method
reshape(u::PArray, dims)

Yields the PArray of the same elements in the given shape. Since a PArray's tree is keyed by linear position and reshaping preserves the linear order, this changes only the shape: it is O(1), and a sparse array stays sparse.

A : may stand for one of the dimensions, as it does for an Array, and takes up whatever the other dimensions leave.

See also: Base.reshape, PArray.

source
Air._currtx_slot — Method
_currtx_slot()

Yields the calling task's slot holding its running Transaction; see currtx. This is part of Air's internal/private implementation details.

source
Air._pairtypes — Method
_pairtypes(::Type)

Yields the key and value types (K, V) of a two-parameter Pair or Tuple type, or nothing if the type is not one. Reading a pair type's parameters by dispatch rather than through the parameters field keeps this off the runtime reflection path.

source
Air._pheap_swap — Method
_pheap_swap(heap, index, ii, jj, childsecond)

Exchanges the value/weight stored at slots ii and jj of heap, which must be adjacent in the tree (one the parent of the other), repairing the cached subtree-weight totals and the value→slot index. childsecond says whether jj is the child (as in _pheap_fix_down) or the parent (as in _pheap_fix_up).

A swap leaves the shape of the tree alone, so each slot's total must be the weight now sitting in it plus the totals of the children it already had. Both are computed here, before either slot is written, so they cost nothing beyond the two writes the exchange needs anyway.

Computing them from the children is what makes this correct. Adjusting the totals the two slots held before the swap — which is what this used to do — only comes out right if those totals were consistent beforehand, and during a percolation they are not: the caller has already added the weight change to the node it is moving, so the totals along the path are mid-update. The children, by contrast, are untouched at this point, because a percolation rewrites one slot at a time along a single path.

source
Air._ptree_claim — Method
_ptree_claim(u)

Yields a copy of the tree node u that the calling transient owns, or u itself if it is already owned. The copy's cells vector is fresh, so it may be changed in place; u is left untouched.

source
Air._ptree_claimcells — Method
_ptree_claimcells(u, cells)

Yields a cells vector that the calling transient may change in place: cells itself if it already owns the node u, and a copy of it otherwise.

source
Air._ptree_clean — Method
_ptree_clean(u)

Yields a tree equal to u with every node's ownership flag cleared, visiting only the nodes a transient owns.

This is what makes persistent! sound. An owned node is one a transient may change in place, so a tree handed to the caller must not contain any: otherwise a later transient of that tree would see the flag, take the node for its own, and write through it — changing a collection the caller believes is immutable.

The walk is cheap for the same reason the transient is. Every node on the path a transient updates is rebuilt as owned, so the owned nodes form a connected spine from the root down to each claimed leaf: an owned node's ancestors are all owned too. So the walk descends into a child only when the child is owned, and stops everywhere else — it visits exactly the nodes the batch created, no others, and never reaches an untouched subtree. For an append-heavy batch that is a handful of ancestors plus one twig per 64 appends, since consecutive appends claim the same rightmost twig. It rebuilds a node struct per visited node (the id is immutable) but copies no cells vector, since it owns the ones it visits.

source
Air._ptree_tdelete — Method
_ptree_tdelete(u, k)

PTree.delete, except that the nodes it touches end up owned by the calling transient and their cells vectors are shortened in place rather than copied. The minimal-tree collapse delete does — a branch left with one child becomes that child — is preserved; the node handed back for a collapsed branch is a child, which the parent then claims in the ordinary way.

source
Air._ptree_tsetindex — Method
_ptree_tsetindex(u, v, k)

PTree.setindex, except that the nodes it touches end up owned by the calling transient and their cells vectors are reused rather than copied. Yields the new tree, sharing with u everything the operation did not touch.

source
Air._ptreeiter — Method
_ptreeiter(u)

Yields the initial iteration state for the PTree u. The root is pushed immediately, so that an empty path means "exhausted" rather than "not yet started" — with a lazily-seeded stack those two states would be indistinguishable, and an exhausted iterator would start over.

source
Air._ptreeiter_next — Method
_ptreeiter_next(path, todo)

Yields (leafid, value) for the next leaf in ascending leaf order, advancing the state in place, or nothing once the tree is exhausted.

source
Air._wcombine — Method
_wcombine(rule, el, weights)

Yields the weight an element takes, given the weights it was found with. See the note above for what each rule means.

source
Air.actor_main — Method
actor_main(actor)

The actor main function is what actually manages everything behind the scenes in the actor tasks. Note that this function and the task/thread it is running in is the only place that the actor's value can be legally changed. Changing the value anywhere else can result in undefined behavior. The actor's condition should never change.

This function is designed to process all the messages in an actor's queue then to return. When a new thread wants to enqueue a message to an actor that has an empty queue, that thread must spin up the actor_main function to process the items in the queue (this is done by the send function automatically).

This function is part of Air's internal/private implementation details.

source
Air.actor_reset! — Method
actor_reset!(actor, val)

Resets the given actor to have the given value. If the actor is not in an error state, yields nothing and does nothing. Otherwise, yields the ActorExcption object that was cleared. It is required that the actor's mutex be held at ths time this function is called.

This function is considered part of Air's internal/private implementation details.

source
Air.actor_reset — Method
actor_reset(a, s)

If the given Actor object a is in an error state, this function resets the actor to have the new value s then yields the ActorException object that was just cleared. If a is not in an error state, this function simply yields nothing.

This function is considered part of the internal/private interface of Air and shouldn't generally be called outside of it.

source
Air.actor_send! — Method
actor_send!(actor, message)

Enqueues the given message to the given actor and starts the actor's task, if necessary. This function requires that the actor's condition be held. Yields the actor.

This function is considered part of Air's internal/private implementation details.

source
Air.actor_send — Method
actor_send(a, msg)

Adds the given ActorMsg message object msg to the queue of the given Actor object a such that it will be processed in a separate thread. Yields nothing.

This function is considered part of the internal/private interface of Air and shouldn't generally be called outside of it.

source
Air.actor_start! — Method
actor_start!(actor)

If the given Actor object is not in an error state, is not running, and does not have an empty queue, then actor_start!(actor) will start actor's processing task. This should only be called by send when the first message to an empty queue is finalized.

If either the actor is already running or the task is successfully started, then that task is yielded. If the actor has an empty queue, then nothing is returned, and if the actor is in an error state, then an exception is thrown.

Note: It is required that the mutx for the given actor be held when this function is called; otherwise race conditions could be inadvertantely created. The actor_start method locks the actor's mutex then calls this function. Both methods are private to Air and should not generally be used outside of it.

This function is considered part of Air's internal/private implementation details.

source
Air.actor_start — Method
actor_start(actor)

If the given Actor object is not in an error state, is not running, and does not have an empty queue, actor_start(actor) will start actor's processing task. This should only be called by send! when the first message to an empty queue is finalized.

If either the actor is already running or the task is successfully started, then that task is yielded. If the actor has an empty queue, then nothing is returned, and if the actor is in an error state, then an exception is thrown.

This function is considered part of Air's internal/private implementation details.

source
Air.actor_value — Method
actor_value(a)

Yields the in-transaction value for the given Actor object a if there is a running transaction; otherwise yields the current out-of-transaction value of a.

This function is considered part of the internal/private interface of Air and shouldn't generally be called outside of it.

source
Air.cancel! — Function
cancel!(token, reason = nothing)

Cancels the given CancelToken and wakes every task waiting on it; the reason is available as the reason field of the Cancelled exception those tasks throw. Cancelling a token that is already cancelled changes nothing and is not an error; the first reason given is kept. Yields the token.

See also: CancelToken, iscancelled, checkcancel

source
Air.canceltoken — Method
canceltoken()

Yields the calling task's CancelToken, creating it if the task does not have one yet. This is the default token of every blocking Air call, so a task needs to do nothing to be cancellable; to cancel it, another task needs the token itself (typically recorded when the task is created).

See also: setcancaltoken!, cancel!

source
Air.checkcancel — Method
checkcancel(token)

Throws Cancelled if the given CancelToken has been cancelled and yields nothing otherwise. A nothing token is always allowed and does nothing, so checkcancel(token) may be called unconditionally when a token is optional. This is the explicit, non-blocking way to observe cancellation; every blocking Air call checks its token itself.

See also: CancelToken, iscancelled, cancel!

source
Air.currtx — Method
currtx()

Yields the current Transaction object, or nothing if there is no transaction running in the current task.

See also: Transaction

Examples

DocTestSetup = quote
    using Air
end
julia> currtx() === nothing
true

julia> @tx (currtx() === nothing)
false
source
Air.delete — Method
delete(coll, k)

Yields a copy of the collection coll with the item associated with the given index or key k deleted. This function works on dictionaries, vectors, tuples, and sets, and it always yields a copy of the collection without modifying its aguments. For the persistent collections defined in Air, this is efficient, but for most mutable objects, this is O(n).

See also: Base.delete!, insert, pop.

Examples

DocTestSetup = quote
    using Air
end
julia> delete((:a,:b,:c,:d), 2)
(:a, :c, :d)
julia> u = [:a,:b,:c,:d]; delete(u, 1)
3-element Array{Symbol,1}:
 :b
 :c
 :d
julia> s1 = Set([:a,:b,:c,:d]); s2 = delete(s1, :d); (:d in s1, :d in s2)
(true, false)
julia> b = BitArray([0,0,0,1,0]); delete(b, 4)
4-element BitArray{1}:
 0
 0
 0
 0

julia> b[2]
false
source
Air.deleteat — Method
deleteat(u::PVector, i)

Yields the PVector of the elements of u with the element at position i removed, in the same kind of vector as u.

source
Air.depth_to_bitshift — Method
depth_to_bitshift(depth)

Yields the tuple (B0,S) of the first bit index and the shift for the given depth.

source
Air.equalfn — Method
equalfn(x)

If x is an object (such as a PSet or Dict) that has an opinion about equality, equalfn(x) returns the function that it uses.

See also: hashfn

Examples

DocTestSetup = quote
    using Air
end
julia> equalfn(Dict) == isequal
true

julia> equalfn(IdDict) == (===)
true

julia> equalfn(PSet) == isequal
true

julia> equalfn(PArray)
ERROR: ArgumentError: no equalfn for type PArray
source
Air.fail! — Method
fail!(promise, err)

Settles promise as failed with the given error payload and yields err; a later take throws PromiseFailed carrying it. A promise settles exactly once, so failing one that has already been delivered or failed raises ArgumentError. Inside a transaction, the failure is staged like a delivery and happens only if the transaction commits.

See also: Promise, take, isfailed, geterror

source
Air.geterror — Method
geterror(actor)

If the given Actor object is currently in an error state, then yields the ActorException object that describes the error. Otherwise, yields nothing.

See also: Actor, reset, send

Examples

DocTestSetup = quote
    using Air
end
julia> a = Actor{Symbol}(:start_sym)
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> geterror(a) === nothing
true

julia> send(a) do val; error("test") end
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> sleep(1); a
Actor{Symbol}(@JIm7aUS2sOl: --error--)

julia> geterror(a) isa ActorException
true
source
Air.geterror — Method
geterror(promise)

Yields the failure payload of a Promise that was settled as failed with fail!, and nothing if it is pending or delivered. This is the non-throwing counterpart of take, which throws PromiseFailed for a failed promise. Inside the transaction that staged a failure, it reflects the staged settlement. A promise failed with a payload of nothing also yields nothing, so use isfailed to tell that case from a pending or delivered promise.

See also: Promise, isfailed, fail!

source
Air.getpair — Function
getpair(d, k)

If the key k is found in the dictionary d, yields the pair (k => d[k]); otherwise yields missing.

See also: Base.get, Base.getindex.

Examples

DocTestSetup = quote
    using Air
end
julia> d = Dict(:a => 1, :b => 2, :c => 3); getpair(d, :a)
:a => 1

julia> getpair(d, :x)
missing
source
Air.getweight — Method
getweight(h, x)

Yields the weight for the given value x in the given persistent heap or persistent weighted collection h. If h is a weighted dictionary, then x should be the key.

If the key or object x is not found in the collection h, then 0 is returned.

See also: setweight, PWDict, PWSet.

Examples

DocTestSetup = quote
    using Air
end
julia> d = PWSet{Symbol}(:a => 1.0, :b => 2.0, :c => 3.0)
PWSet{Symbol,Float64} with 3 elements:
  :c
  :b
  :a

julia> getweight(d, :a)
1.0
source
Air.hashfn — Method
hashfn(x)

If x is an object (such as a PSet or Dict) that has an opinion about how it hashes objects, hashfn(x) returns the function that it uses. It is sufficient in almost all circumstances to define equalfn(T); the hashfn should always match the equalfn regardless.

See also: equalfn

Examples

DocTestSetup = quote
    using Air
end
julia> hashfn(Dict) == hash
true

julia> hashfn(IdDict) == objectid
true

julia> hashfn(PSet) == hash
true

julia> hashfn(PArray)
ERROR: ArgumentError: no equalfn for type PArray
source
Air.highmask — Method
highmask(bitno, T)
highmask(bitno)

Yields a mask of (unsigned integer) type T with all bits above the given bit number set to true and all bits below that number set to false. The bit itself is set to true. Bits are indexed starting at 0.

The default value of T is the PTree hash type (HASH_T).

highmask(bitno) is equal to ~lowmask(bitno).

source
Air.insert — Function
insert(coll, idx, val)

Yields a copy of the given collection coll with the given value cal inserted at the given index. Roughly equivalent to insert!(copy(arr), idx, va): the insert function never modifies its arguments and always yields a copy. For the persistent collections defined in Air, this operation is efficient, but for most mutable objects, this is O(n).

See also: Base.insert!, push, delete.

Examples

DocTestSetup = quote
    using Air
end
julia> insert((:a,:b,:c,:d), 2, :x)
(:a, :x, :b, :c, :d)
julia> u = [:a,:b,:c,:d]; insert(u, 1, :x)
5-element Array{Symbol,1}:
 :x
 :a
 :b
 :c
 :d

julia> length(u)
4
julia> b = BitArray([0,0,0,0]); insert(b, 2, 1)
5-element BitArray{1}:
 0
 1
 0
 0
 0

julia> b[2]
false
source
Air.isfailed — Method
isfailed(promise)

Yields true if the given Promise has been failed with fail!, and false if it is pending or delivered. Inside the transaction that staged a failure, it reflects the staged settlement, like isready.

See also: Promise, geterror, PromiseFailed

source
Air.lockall — Function
lockall(f, l1, l2, ...)

Locks all of the lockable objects l1, l2, etc. then runs f, unlocks the objects, and returns the return value of f().

See also: Threads.ReentrantLock

Examples

DocTestSetup = quote
    using Air
end
julia> (r1, r2, r3) = [ReentrantLock() for _ in 1:3]
       lockall(r1, r2, r3) do; :success end
:success

julia> lockall([r1, r2, r3]) do; :success end
:success

julia> lockall((r1, r2, r3)) do; :success end
:success
source
Air.lowmask — Method
lowmask(bitno, T)
lowmask(bitno)

Yields a mask of (unsigned integer) type T with all bits above the given bit number set to false and all bits below that number set to true. The bit itself is set to false. Bits are indexed starting at 0.

The default value of T is the PTree hash type (HASH_T).

lowmask(bitno) is equal to ~highmask(bitno).

source
Air.pdiagm — Method
pdiagm(v)
pdiagm(k => v, ...)

Yields the PArray with the vector v on its diagonal — or, for the second form, on the k-th diagonal, as SparseArrays.spdiagm does. Entries equal to zero are not stored, so the result is diagonal in the sparse sense as well as the structural one.

source
Air.pdict_view — Method
pdict_view(d::AbstractPWDict) -> AbstractPDict{K,Tuple{V,W}}

Yields a view of a weighted dictionary as a dictionary of key => (value, weight) pairs, with the same guarantees as pset_view: O(1) to build, one pass to traverse, read-only, and in the storage's order rather than the weights-sorted one.

One thing the view cannot avoid: the weight is stored beside the key in the heap, but the value lives in the dictionary, so every element costs a dictionary lookup on top of the pass. That is a property of the split rather than of the view. Iterating the dictionary and looking each weight up instead is worse rather than better — measured at 428 KB and 468 us per thousand against 32 bytes and 57 us here — because the dictionary's own iteration and the heap index's lookup are both paid per element either way, and the index lookup is the dearer of the two.

source
Air.persistent! — Method
persistent!(t)

Yields a persistent collection holding what the transient t currently holds, in O(1). t must not be used afterwards.

source
Air.pfill — Method
pfill(val, dims...)

Yields a persistent array (PArray) of values exactly as done by the fill() function.

See also pones, pzeros, fill

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> pfill(NaN, 3)
3-element PArray{Float64,1}:
 NaN
 NaN
 NaN
julia> pfill(:abc, 2, 3)
2×3 PArray{Symbol,2}:
 :abc  :abc  :abc
 :abc  :abc  :abc
source
Air.pones — Method
pones(dims...)
pones(T, dims...)

Yields a persistent array (PArray) of ones exactly as done by the ones() function.

See also pzeros, pfill, ones

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> pones(Integer, 3)
3-element PArray{Integer,1}:
 1
 1
 1
julia> pones(Bool, (1,2))
1×2 PArray{Bool,2}:
 1  1
julia> pones(2, 3)
2×3 PArray{Float64,2}:
 1.0  1.0  1.0
 1.0  1.0  1.0
julia> pones((1, 1, 1, 1))
1×1×1×1 PArray{Float64,4}:
[:, :, 1, 1] =
 1.0
source
Air.pop — Function
pop(coll)

Yields a tuple (last, most) where last is the last element of the given collection and most is a duplicate tuple of all but the last element of tup. This is basically a persistent equivalent to the pop! function that never modifies the given coll. For persistent collections in Air, these operations are efficient, but for most mutable types, it is O(n).

pop(coll, key[, default])

Similar to pop!, pops the specific key from the given collection coll and yields (val, rest) where val is the value associated with key in coll and rest is the remainder of the collection without key. If the key is not in coll, then default is yielded or an error is thrown.

See also: Base.pop!, push, popat, delete.

Examples

DocTestSetup = quote
    using Air
end
julia> pop((:a,:b,:c,:d))
(:d, (:a, :b, :c))

julia> u = [:a,:b,:c,:d]; pop(u)
(:d, [:a, :b, :c])

julia> length(u)
4

julia> s = Set([:a, :b, :c]); pop(s, :c)
(:c, Set([:a, :b]))

julia> :c in s
true
julia> d = Dict(:a => 1, :b => 2, :c => 3); pop(d, :c)
(3, Dict(:a => 1,:b => 2))

julia> d[:c]
3
source
Air.popat — Function
popat(coll, k)

Yields a tuple (el, rest) where el is the kth element of the given collection coll and rest is a duplicate of all but the kth element of coll. This is basically a persistent equivalent to the popat! function that never modifies the given coll. For persistent collections in Air, these operations are efficient, but for most mutable types, it is O(n).

See also: Base.popat!, pop, popfirst, delete.

Examples

DocTestSetup = quote
    using Air
end
julia> popat((:a,:b,:c,:d), 2)
(:b, (:a, :c, :d))

julia> u = [:a,:b,:c,:d]; popat(u, 3)
(:c, [:a, :b, :d])

julia> length(u)
4
source
Air.popfirst — Function
popfirst(coll)

Yields a tuple (first, rest) where first is the first element of the given collection and rest is a duplicate tuple of all but the first element of tup. This is basically a persistent equivalent to the popfirst! function that never modifies the given coll. For persistent collections in Air, these operations are efficient, but for most mutable types, it is O(n).

See also: Base.popfirst!, pushfirst, popat, delete.

Examples

DocTestSetup = quote
    using Air
end
julia> popfirst((:a,:b,:c,:d))
(:a, (:b, :c, :d))

julia> u = [:a,:b,:c,:d]; popfirst(u)
(:a, [:b, :c, :d])

julia> length(u)
4
source
Air.prand — Method
prand(dims...)
prand(m, n, p)

Yields a PArray of random values: of the given dimensions, as rand would, or of size m by n with each position set with probability p, as sprand would. The sparse form has a default of zero, so only the positions it fills are stored.

source
Air.pset_view — Method
pset_view(s::AbstractPWSet) -> AbstractPSet{Tuple{T,W}}

Yields a view of a weighted set as a set of (element, weight) pairs. Building one is O(1), and traversing it is one pass over the collection's storage, where traversing the set itself yields the same elements ordered by weight at a sort's cost. For a thousand elements the view is 32 bytes and 11 us against the ordered traversal's 16.5 KB and 54 us.

The view iterates in the order of the underlying storage, not the weights-sorted order that PHeap's own traversal gives. That is what it trades for the difference in cost.

It is read-only, having no storage of its own: push, delete and empty raise.

source
Air.psparse — Method
psparse(I, J, V, m, n)
psparse(A; default=zero(eltype(A)))

Yields the PArray holding V[k] at position (I[k], J[k]), of size m by n, as SparseArrays.sparse does — or, from an array, one holding the entries of A that are not zero, again as sparse(A) does.

Unlike a SparseArray, the array it yields may have a default value of its own; default names it, and an entry equal to it is not stored at all. So psparse(A; default=NaN) yields an array that reads as NaN wherever A is NaN, rather than storing those positions — which is the only way to express what a SparseArray cannot.

Repeated positions are an error rather than being summed, since a persistent array cannot distinguish "written twice" from "written once".

Examples

DocTestSetup = quote
    using Air
end
julia> psparse([1, 2], [2, 3], [10.0, 20.0], 2, 3)
2×3 PMatrix{Float64}:
 0.0  10.0   0.0
 0.0   0.0  20.0
source
Air.ptree_cellindex! — Method
ptree_cellindex!(ptree, leafid)
ptree_cellindex!(nodeid, bits, leafid)

Yields the index into the ptree's cell vector of the child containing the leaf with the given id. If the ptree does not contain the given leafid because the appropriate bit is not set then 0 is returned. However, unlike the function ptree_cellindex(), this function does not check whether or not the given leafid is in the set of possible children of the tree.

source
Air.ptree_cellindex — Method
ptree_cellindex(ptree, leafid)
ptree_cellindex(nodeid, bits, leafid)

Yields a tuple (present, bitindex, cellindex) in which [1] present is a boolean indicating whether a ptree containing the given leafid or the leaf itself is a child of the given ptree; [2] bitindex is the index into ptree's bits integer for the given leaf, and cellindex is the inndex innto the ptree's cell array where that child is or would be found. If the leafid is outside of the given ptree (i.e., it cannot exist beneath this ptree) then the cellindex returned returned is 0, but the bitindex will still match the appropriate shift for the ptree's depth.

source
Air.ptree_cellkey — Method
ptree_cellkey(ptree, childidx)

Yields the leafid (a HASH_T value) of the key that goes with the particular child index that is given. This only works correctly for twig nodes.

source
Air.ptree_depth — Method
ptree_depth(nodeaddr)

Yields the depth of the node with the given node address. This depth is in the theoretical complete tree, not in the reified tree represented with memory.

source
Air.ptree_id — Method
ptree_id(minleaf, depth)

Yields the node-id for the node whose minimum leaf and depth are given.

source
Air.ptree_isbeneath — Method
ptree_isbeneath(nodeid, leafid)

Yields true if the given leafid can be found beneath the given node-id.

source
Air.ptree_maxleaf — Method
ptree_maxleaf(nodeid)

Yields the maximum child leaf index assiciated with the given nodeid.

source
Air.ptree_minleaf — Method
ptree_minleaf(nodeid)

Yields the minimum child leaf index associated with the given nodeid.

source
Air.ptree_minmaxleaf — Method
ptree_minmaxleaf(nodeid)

Yields the (min, max) child leaf index assiciated with the given nodeid.

source
Air.ptree_owned — Method
ptree_owned(nodeid)
ptree_owned(id, owned)

Whether the node with the given id is owned by a transient (see PTREE_OWNED_FLAG), or the same id with that flag set or cleared.

source
Air.ptree_parentid — Method
ptree_parentid(nodeid0)

Yields the node-id of the parent of the given node. Note that node 0 (the tree's theoretical root) has no parent. If given a node id of 0, this function will return an arbitrary large number.

source
Air.push — Function
push(coll, val)
push(coll1, val1, val2...)

Yields a copy of the given collection coll with the given value val appended. This function is essentially a persistent equivalent of the push! function that never modifies the object coll. For persistent collections in the Air library, this operation is efficient, but for most mutable objects, it is O(n).

See also: Base.push!, pop, Base.setindex!.

Examples

DocTestSetup = quote
    using Air
end
julia> push((:a,:b,:c,:d), :e)
(:a, :b, :c, :d, :e)
julia> u = [:a,:b,:c,:d]; push(u, :e)
5-element Array{Symbol,1}:
 :a
 :b
 :c
 :d
 :e
julia> s = Set([:a, :b, :c]); s2 = push(s, :d);

julia> (length(s2), :a in s2, :d in s2, :e in s2)
(4, true, true, false)
julia> d = Dict(:a => 1, :b => 2); d2 = push(d, :c => 3, :d => 4);

julia> (length(d2), d2[:c], d2[:d])
(4, 3, 4)
source
Air.pushfirst — Function
pushfirst(coll, val)
pushfirst(coll1, val1, val2...)

Yields a copy of the given collection coll with the given value val prepended. This function is essentially a persistent equivalent of the pushfirst! function that never modifies the object coll. For persistent collections in the Air library, this operation is efficient, but for most mutable objects, it is O(n).

See also: Base.pushfirst!, popfirst, Base.setindex!, push.

Examples

DocTestSetup = quote
    using Air
end
julia> pushfirst((:a,:b,:c,:d), :e)
(:e, :a, :b, :c, :d)

julia> u = [:a,:b,:c,:d]; pushfirst(u, :e)
5-element Array{Symbol,1}:
 :e
 :a
 :b
 :c
 :d

julia> length(u)
4
source
Air.pzeros — Method
pzeros(dims...)
pzeros(T, dims...)

Yields a persistent array (PArray) of zeros exactly as done by the zeros() function.

See also pones, pfill, zeros

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> pzeros(Integer, 3)
3-element PArray{Integer,1}:
 0
 0
 0
julia> pzeros(Bool, (1,2))
1×2 PArray{Bool,2}:
 0  0
julia> pzeros(2, 3)
2×3 PArray{Float64,2}:
 0.0  0.0  0.0
 0.0  0.0  0.0
julia> pzeros((1, 1, 1, 1))
1×1×1×1 PArray{Float64,4}:
[:, :, 1, 1] =
 0.0
source
Air.send — Method

send(fn, actor)

The send() method can be used to send a function to an actor. The function fn is put in the actor's queue, which is processed sequentially by a separate thread. In the actual function call that is evaluated in this separate thread, the function is called as fn(actor_value), and its return value becomes the new stored value of the actor—unless the actor has a post-processing function, in which case the post-processor's return value is stored instead (see Actor).

The function is called in the latest world, so a function that was defined after the actor's processing thread was started—a closure entered at the REPL, for instance—can be sent to it; without that, the actor would raise a MethodError saying the applicable method is too new.

Note that the value of an actor when send() is called does not make any guarantee about its value when the function that is sent gets evaluated—other functions may be processed in the interim.

If send() is called inside of a transaction, then the function is not immediately queued but rather is held until the transaction successfully completes. Once this happens, all sends are dispatched simultaneously such that two sends to the same actor during the same transaction will always run back-to-back in the actor's processing thread.

This includes a send() made from a volatile finalizer or a promise delivery callback, which run in the transaction's finalize phase with no transaction active (currtx() === nothing): the message is staged in the transaction that is finalizing and queued only if it commits. A transaction that loses validation, or that aborts, sends nothing.

Important. Due to the design pattern described in the previous paragraph, one should never wait on the result of a function sent to an actor during a transaction. This mistake might be made, for example, by sending an actor, during a transaction, a function that delivers some result to a Promise object. If the sending thread then waits on that promise while still in the same transaction then it will deadlock because the sent function is not put in the actor's queue until the enclosing transaction is finished. Thus the promise will never receive the result from the actor.

TL;DR—When you touch an actor in a transaction, it's value freezes inside the transaction. So if the transaction somehow waits for the actor to update or to do something, that transaction will deadlock.

source
Air.setcancaltoken! — Method
setcancaltoken!(token)

Sets the calling task's cancellation token to token, which may be nothing to give the task a fresh token on its next canceltoken() call, and yields it.

See also: canceltoken, CancelToken

source
Air.seterrorhandler! — Method
seterrorhandler!(actor, fn)

Sets the error-handler function of the given Actor to fn, or clears it when fn is nothing, and yields the actor. The change takes effect for the next function the actor dequeues; a function that is already running is unaffected. This is actor configuration rather than actor state: the change is immediate and is not rolled back by a transaction.

See also: geterrorhandler, setpostprocess!

source
Air.setfilter! — Method
setfilter!(vol, fn)

Sets the filter-function associated with the Volatile object vol. Any time that the vol is set (vol[] = x) the filter-function is called and the value saved in vol is instead fn(x). This must be called within a transaction.

source
Air.setfinalize! — Method
setfinalize!(vol, fn)

Sets the finalize-function associated with the Volatile object vol. Any time that a transaction contains a change to vol, immediately prior to making an attempt at committing the transaction, the finalize function is called and the value committed to vol is instead fn(x) where x is the value set to vol in the transaction. This funvtion must also be called within a transaction.

source
Air.setindex — Method
setindex(coll, val, index)

Yields a copy of the given collection coll with the value val set at the given index. This is a persistent version of setindex! and works for most collection types including Arrays, Dicts, IdDicts, and Air's persistent versions of these.

Note that setindex() always returns a copy of the argument coll or fails. For Air's persistent collections these operations are efficient, but for the mutable counterparts, they are typically O(n).

See also: Base.setindex!, push.

Examples

DocTestSetup = quote
    using Air
end
julia> setindex((:a,:b,:e,:d), :c, 3)
(:a, :b, :c, :d)
julia> u = [:a,:b,:e,:d]; v = setindex(u, :c, 3)
4-element Array{Symbol,1}:
 :a
 :b
 :c
 :d

julia> u == v
false

julia> u[3]
:e
julia> d1 = Dict(); setindex(d1, 10, :a)
Dict{Any,Any} with 1 entry:
  :a => 10

julia> d1
Dict{Any,Any}()
source
Air.setpostprocess! — Method
setpostprocess!(actor, fn)

Sets the post-processing function of the given Actor to fn, or clears it when fn is nothing, and yields the actor. The change takes effect for the next function the actor dequeues; a function that is already running is unaffected. This is actor configuration rather than actor state: the change is immediate and is not rolled back by a transaction.

See also: getpostprocess, seterrorhandler!

source
Air.setvars — Function
setvars(f, vardict)

Runs the function f in a context in which all Var objectss have been bound to the values given in the dictionary vardict. The keys of this dictionary must be Var objects. Once the function f has finished running, the Var objects are reverted to their calling-frame values. The current vardict object can be obtained by calling vars().

See also: Var, @var, vars, withvars, wrapwithvars, wrapsetvars

Examples

DocTestSetup = quote
    using Air
end
julia> v = Var{Symbol}(:initval)
Var{Symbol}(@hxC65AWl: :initval; init=:initval)

julia> v[]
:initval

julia> setvars(IdDict(v => :newval)) do; v[] end
:newval
source
Air.setweight — Method
setweight(h, x, w)

Sets the weight of the value x in the given persistent heap or persistent weighted collection object h to be w and yields the new updated version of h. If x is not already in the object h, then an error is thrown.

See also: getweight, PWDict, PWSet.

Examples

DocTestSetup = quote
    using Air
end
julia> d = PWSet{Symbol}(:a => 1.0, :b => 2.0, :c => 3.0)
PWSet{Symbol,Float64} with 3 elements:
  :c
  :b
  :a

julia> setweight(d, :a, 4.0)
PWSet{Symbol,Float64} with 3 elements:
  :a
  :c
  :b
source
Air.sleep_interruptible — Method
sleep_interruptible(seconds; token = canceltoken())

Sleeps for the given number of seconds, but throws Cancelled as soon as the given token is cancelled. Unlike Base.sleep, this cannot leave a task uninterruptible. Cancellation wins if it races the end of the sleep.

This is part of Air's internal interface: it is not exported, because exporting sleep would clash with Base.sleep for anyone who does using Air. AirLang calls it qualified as Air.sleep_interruptible.

See also: CancelToken, canceltoken

source
Air.snapshot — Method
snapshot(stream) -> Vector{T}

Yields a copy of the values the given Stream is holding, oldest first, without blocking and without removing them. The copy is independent of the stream, so it may be kept, mutated or serialized; whether the stream was open and what its bound is are reported separately by isopen and maxsize.

A snapshot is what Stream{T}(items; maxsize) puts back: passing it to the constructor creates a new stream holding the same values, and close on that stream finishes restoring a closed one.

Inside a transaction the snapshot is the transaction's own view: it includes the values the body has staged with put! and excludes those it has staged with take, so it agrees with length, isempty and isready. Like those, it is an advisory snapshot rather than a read that is validated at commit.

See also: Stream, take

Examples

DocTestSetup = quote
    using Air
end
julia> s = Stream{Int}(maxsize = 4); put!(s, 1); put!(s, 2);

julia> snapshot(s)
2-element Vector{Int64}:
 1
 2

julia> take(s)          # a snapshot consumes nothing
1

julia> snapshot(s)
1-element Vector{Int64}:
 2
source
Air.splice — Method
splice(u::PVector, i, n)

Yields the PVector of the elements of u with the n elements starting at position i removed. This is the persistent counterpart of Base.splice!'s removal form, which is what splice! does when it is given no replacements.

source
Air.take — Method
take(promise; timeout = nothing, token = canceltoken())

Yields the value delivered to the given Promise object after suspending the current thread to wait for the value if necessary, and throws PromiseFailed if the promise was failed. If the current transaction has staged a settlement for the promise, that settlement is used without waiting; see Promise.

timeout bounds the wait in seconds: if the promise is still pending when it expires, take throws ReaderTimeout. A settled promise is returned or thrown immediately, even with timeout = 0.

token is the CancelToken to observe; the default is the calling task's own token, see canceltoken. If the token has been cancelled, take throws Cancelled even when the promise is already settled, so pass token = nothing to wait regardless — in the cleanup of a cancelled task, for example. Inside a transaction, a take that has to wait is a deadlock: the delivery it is waiting for cannot happen until the body returns. (Waiting on a Stream inside a transaction is not a deadlock in the same way; see its docstring.)

DocTestSetup = quote
    using Air
end
julia> p = Promise()
Promise{Any}(<...>)

julia> put!(p, :done)
:done

julia> p
Promise{Any}(:done)

julia> take(p)
:done
source
Air.take — Method
take(stream; timeout = nothing, token = canceltoken())

Removes and yields the oldest value of the given Stream, suspending the current task until a value is available if necessary. A closed stream that still holds values yields them, and then throws StreamClosed.

timeout bounds the wait in seconds: if no value is available when it expires, take throws ReaderTimeout. token is the CancelToken to observe, as for take(::Promise); cancellation wins even over an available value. Inside a transaction, take is staged and the value is removed only if the transaction commits; the transaction also sees its own staged writes. A take that has to wait inside a transaction waits normally, but a stream created in that transaction raises StreamDeadlock instead.

See also: Stream, put!, close

source
Air.transient — Method
transient(coll)

Yields a transient (mutable) counterpart of coll, which shares its structure and is O(1). Make updates through the transient, then take the persistent result with persistent!. coll is not modified, and must not be used afterwards if you keep updating the transient.

source
Air.tx — Method
tx(fn)

Runs the given function in a transaction and yields the result. The function fn is called as fn() without arguments.

source
Air.tx_actordata — Method
tx_actordata(a)

Yields the in-transaction data for the Actor object a, or nothing if no transaction is running. An actor in an error state yields data whose tx_value is the ActorException; it is getindex that raises on it, not this function.

This function is considered part of the internal/private interface of Air and shouldn't generally be called outside of it.

source
Air.vars — Function
vars()

Yields an IdDict{Var,Any} object that contains a mapping of all Var objects whose current in-task value is not its default value to that Var's currently assigned value. The dictionary returned by vars may be later restored using the function setvars.

See also: setvars, withvars, wrapwithvars, Var.

Examples

DocTestSetup = quote
    using Air
end
julia> vars()
IdDict{Var,Any}()
julia> @var v = :test::Symbol
Var{Symbol}(@h4G6oRR9s: :test; init=:test)

julia> withvars(v => :temp) do; vars() end
IdDict{Var,Any} with 1 entry:
  Var{Symbol}(@AaE16J5Pic8) => :temp
source
Air.voldata_finalize — Method
voldata_finalize(voldata)

Calls the finalize function of the given volatile state object (of type VolatileData) if necessary. Yields the new VolatileData object, which may be voldata if there is no finalizer or nothing has changed.

This function is a private/internal implementation detail of the Air library.

source
Air.withvars — Function
withvars(f, var1 => val1, var2 => val2...)
withvars(f, vardict)

Runs the function f in a context in which the given Vars have been bound to the given values. In the vase of a dictionary passed as the second argument, the keys must be Var objects. Once the function f has finished running, the Var objects are reverted to their calling-frame values.

See also: Var, @var, vars, setvars, wrapwithvars, wrapsetvars

Examples

DocTestSetup = quote
    using Air
end
julia> v = Var{Symbol}(:initval)
Var{Symbol}(@cFgU9Kqe8: :initval; init=:initval)

julia> v[]
:initval

julia> withvars(v => :newval) do; v[] end
:newval
source
Air.wrapsetvars — Function
wrapsetvars(f, vardict)

Equivalent to setvars(f, args...) except that instead of running f immediately in the context of the modified Vars, yields a wrapper around f that, when called, passes all arguments to f, which is run using the given variable bindings in the dictionary vardict.

See also: Var, @var, vars, withvars, setvars, wrapwithvars

Examples

DocTestSetup = quote
    using Air
end
julia> v = Var{Int}(0)
Var{Int64}(@F6ku5d8: 0; init=0)

julia> v[]
0

julia> vs = withvars(v => 2) do; vars() end; length(vs)
1

julia> f = wrapsetvars(vs) do x; x + v[] end; f(10)
12
source
Air.wrapwithvars — Function
wrapwithvars(f, var1 => val1, var2 => val2...)
wrapwithvars(f, vardict)

Equivalent to withvars(f, args...) except that instead of running f immediately in the context of the modified Vars, yields a wrapper around f that, when called, passes all arguments to f, which is run using the current variable bindings plus any given bindings. Notable, wrapwithvars(f) will create a wrapped version of f that uses the Var bindings in the current task.

See also: Var, @var, vars, withvars, setvars, wrapsetvars

Examples

DocTestSetup = quote
    using Air
end
julia> v = Var{Int}(0)
Var{Int64}(@JIm7aUS2sOl: 0; init=0)

julia> v[]
0

julia> f = wrapwithvars(v => 2) do x; x + v[] end; f(10)
12
source
Base.cat — Method
cat(u::PArray, others::PArray...; dims)

The persistent counterpart of Base.cat in the two cases it reduces to: dims=1 is vcat and dims=2 is hcat. The block form, dims=(1,2), is not implemented, and says so rather than quietly producing something else.

source
Base.circshift — Method
circshift(u::PVector, n)

Yields the PVector of the elements of u shifted by n positions, with the same default.

source
Base.close — Method
close(stream)

Closes the given Stream, waking every task waiting on it. Closing is idempotent. Afterwards a put! throws StreamClosed, and a take yields the values that remain and then throws it too. Inside a transaction the close is staged and happens only if the transaction commits; isopen sees the staged close in the transaction that staged it.

See also: Stream, StreamClosed, isopen

source
Base.filter — Method
filter(f, d::AbstractPDict)

Yields the persistent dictionary of the entries of d for which f is true. f is applied to each key => value pair, as in Base.filter; unlike Base.filter — which builds a mutable Dict, and cannot build one of these at all — the result is the same kind of dictionary as its argument.

source
Base.filter — Method
filter(f, s::AbstractPSet)

Yields the persistent set of the elements of s for which f is true, as the same kind of set as its argument. (A weighted set has its own method, in pwset.jl, which keeps each element's weight.)

source
Base.filter — Method
filter(f, u::PVector)

Yields the PVector of the elements of u for which f is true, in order. The result has the same default as u, so an element equal to the default is not stored in it — filtering a sparse vector does not materialise the positions it keeps.

This is the persistent counterpart of Base.filter, which would otherwise return a mutable Array.

source
Base.hcat — Method
hcat(u::PMatrix, rest::AbstractMatrix...)

Yields the PArray{Float64,2}-style matrix of the given matrices, side by side. The operands must have the same number of rows. As with vcat, the result's default is the first operand's.

This replaces the SparseArrays method that would otherwise be reached and return a mutable SparseMatrixCSC. As with vcat, the element types are restricted to Number so that this method is strictly narrower than SparseArrays' rather than ambiguous with it.

source
Base.intersect — Method
intersect(s::AbstractPSet, t::AbstractPSet)

Yields the persistent set of the elements common to both, as the same kind of set as the first argument.

source
Base.intersect — Method
intersect(s::AbstractPWSet, t::AbstractPWSet; weight=:first)

Yields the persistent weighted set of the elements both hold, each taking its weight from weight.

source
Base.isopen — Method
isopen(stream)

Yields false if the given Stream has been closed and true otherwise. Inside a transaction, a close staged by that transaction is visible, so this is false there as well.

See also: Stream, close

source
Base.permutedims — Method
permutedims(u::PArray, perm)

Yields the PArray whose dimensions are those of u rearranged by perm. As with reshape this only moves the stored entries — a permutation of the axes changes which linear position each one has, so the tree is rebuilt, but the default is carried along and a sparse array stays sparse.

This is the persistent counterpart of Base.permutedims.

source
Base.put! — Method
put!(stream, value; timeout = nothing, token = canceltoken())

Appends value to the given Stream and yields it, converted to the stream's element type. If the stream has a maxsize and is full, put! suspends the current task until a take makes room. A closed stream throws StreamClosed.

timeout bounds the wait in seconds and throws ReaderTimeout when it expires; token is the CancelToken to observe, as for take. Inside a transaction, the append is staged and happens only if the transaction commits, and a put! after a staged close throws StreamClosed. A put! that has to wait on a stream created in that transaction raises StreamDeadlock instead.

See also: Stream, take, close

source
Base.repeat — Method
repeat(u::PVector, n::Integer)
repeat(u::PVector; inner=1, outer=1)

Yields the PVector of the elements of u repeated: the whole vector n (or outer) times, or each element inner times, as Base.repeat does. The result has the same default, so repeating a sparse vector keeps its sparse value.

source
Base.replace — Method
replace(d::AbstractPDict, pairs...)

Yields the persistent dictionary with each of the values named among pairs replaced, as Base.replace does — but as a dictionary of the same kind as its argument rather than a mutable Dict.

source
Base.replace — Method
replace(s::AbstractPSet, pairs::Pair...)

Yields the persistent set with each of the elements named among pairs replaced, as the same kind of set as its argument.

source
Base.reset — Method
reset(actor, x)

If the given actor is in an error state, this (1) resets it, meaning it will start handling sent messages again, (2) gives it the new initial value x, and (3) yields the ActorException object that was just clared. If the actor is not in an error state, this just yields nothing.

source
Base.reverse — Method
reverse(u::PVector)

Yields the PVector of the elements of u in the opposite order, with the same default. Only the stored entries move, so the cost is the number of entries rather than the length.

source
Base.rotl90 — Method
rotl90(m::PMatrix)

Yields the PArray of m rotated a quarter-turn to the left, as LinearAlgebra.rotl90 does. That both rotates and transposes, so the result's shape is m's reversed. The stored entries move and the default is carried, so a sparse matrix stays sparse.

source
Base.setdiff — Method
setdiff(s::AbstractPSet, t::AbstractPSet)

Yields the persistent set of the elements of s that are not in t, as the same kind of set as the first argument.

source
Base.setdiff — Method
setdiff(s::AbstractPWSet, t::AbstractPWSet; weight=:first)

Yields the persistent weighted set of the elements of s that are not in t. Each is held by only one of the arguments, so weight does not come into it — a caller's own function is still called, as its contract says.

source
Base.sort — Method
sort(u::PVector; kws...)

Yields the PVector of the elements of u in sorted order, with the same default as u — so a sparse vector stays sparse, its default value sorting to wherever it sorts among the others.

source
Base.symdiff — Method
symdiff(s::AbstractPSet, t::AbstractPSet)

Yields the persistent set of the elements in exactly one of the two, as the same kind of set as the first argument.

source
Base.symdiff — Method
symdiff(s::AbstractPWSet, t::AbstractPWSet; weight=:first)

Yields the persistent weighted set of the elements in exactly one of the two. As with setdiff, each is held by only one argument.

source
Base.union — Method
union(s::AbstractPSet, others::AbstractPSet...)

Yields the persistent set of every element of the given sets, as the same kind of set as the first argument.

source
Base.union — Method
union(s::AbstractPWSet, others::AbstractPWSet...; weight=:first)

Yields the persistent weighted set of every element of the given sets. An element that several of them hold takes its weight from weight; see the note in this file for what that may be. An element held by only one keeps that one's weight.

source
Base.unique — Method
unique(u::PVector)

Yields the PVector of the elements of u with duplicates removed, keeping the first occurrence of each in the order u has them — the same order Base.unique keeps, which is why this collects by position rather than by walking the tree.

source
Base.vcat — Method
vcat(u::PVector, rest::AbstractVector...)

Yields the PVector of the given vectors, one after another. The result's default is the first operand's, so concatenating sparse vectors stays sparse; an operand whose default differs from it is stored explicitly, as its default value is an explicit value of the result.

This replaces the SparseArrays method that would otherwise be reached and return a mutable SparseVector, which is why the element types are restricted to Number: SparseArrays defines its own vcat over AbstractVecOrMat{<:Number}, and a method over the same arguments but without that restriction is not more specific than it, so the call would be ambiguous rather than intercepted. With it, this method is strictly narrower in both arguments. A PVector of a non-Number element type is left to Base, and its vcat is an Array.

source
Base.vcat — Method
vcat(u::PMatrix, rest::AbstractMatrix...)

Yields the matrix of the given matrices, one above another. The operands must have the same number of columns. As with the vectors, the result's default is the first operand's, and the element types are restricted to Number for the same reason — to be strictly narrower than SparseArrays' method rather than ambiguous with it.

source
LinearAlgebra.triu — Method
triu(m::PMatrix, k::Integer = 0)

Yields the PArray of m with every entry below the k-th diagonal set to zero, as LinearAlgebra.triu does, keeping m's shape.

Note that this zeroes those entries rather than setting them to m's default. Where the default is zero the two are the same and only the stored entries need touching; where it is not, every position below the diagonal has to be stored as an explicit zero, since reading one would otherwise give the default.

source
SparseArrays.dropzeros — Method
dropzeros(p::PArray)

Drops explicit values of the given array p that are equal to the array's default value. This differs from the SparseArrays implementation of dropzeros() only in that PArrays allow arbitrary default values, while SparseArrays allow only the default value of zero.

Note that under most circumstances, a PArrray will not encode explicit zeros, so this function typically returns the object p untouched.

See also: SparseArrays.nnz, SparseArrays.findnz, PArray.

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> u = PVector{Int}([0,1,2,3])
4-element PArray{Int64,1}:
 0
 1
 2
 3

julia> dropzeros(u) === u
true

julia> v = setindex(u, 0, 3)
4-element PArray{Int64,1}:
 0
 1
 0
 3

julia> dropzeros(v) === v
true
source
SparseArrays.findnz — Method
findnz(p::PArray)

Yields the explicitly set elements of the given persistent array p. This method is identical to the typical SparseArrays implementation of findnz() except that it respects the arbitrary default-value that persistent arrays are allowed to have rather than assuming that this value is a zero, as is done in the SparseArrays library.

Note that under most circumstances, a PArray will not encode explicit zeros, so this function typically returns indices and values for all values that aren't equal to the default value of the array p (which is zero by default).

See also: SparseArrays.nnz, PArray.

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> u = PVector([0,10,20,30])
4-element PArray{Int64,1}:
  0
 10
 20
 30

julia> findnz(u)
([1, 2, 3, 4], [0, 10, 20, 30])
julia> u = setindex(PVector(0.0, 4), 20, 2)
4-element PArray{Float64,1}:
  0.0
 20.0
  0.0
  0.0

julia> findnz(u)
([2], [20.0])
source
SparseArrays.nnz — Method
nnz(p::PArray)

Yields the number of explicitly set values in the persistent array p, regardless of the number that are zero. This is different from the sparse-array library only in that persistent arrays support arbitrary default values instead of supporting only the value zero. Thus this counts explicit values instead of non-zero values.

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> u = PVector{Int}(0, (4,))
4-element PArray{Int64,1}:
 0
 0
 0
 0

julia> v = setindex(u, 2, 3)
4-element PArray{Int64,1}:
 0
 0
 2
 0

julia> nnz(v)
1

julia> nnz(u)
0
source
SparseArrays.nonzeros — Method
nonzeros(p::PArray)

Yields the explicitly set values of the given persistent array p. This method is identical to the typical SparseArrays implementation of nonzeros() for its sparse array classes except that it returns a persistent array of values and that it respects the arbitrary default-value that persistent arrays are allowed to have rather than assuming that this value is a zero, as is done in the SparseArrays library.

Note that because PArrays don't typically store values equal to their default value explicitly, this will typically yield a vector of every non-default value in the array.

See also: SparseArrays.findnz, SparseArrays.nnz, PArray.

Examples

DocTestSetup = quote
    using Air, SparseArrays
end
julia> u = PVector([0,10,20,30])
4-element PArray{Int64,1}:
  0
 10
 20
 30

julia> nonzeros(u)
4-element PArray{Int64,1}:
  0
 10
 20
 30
julia> u = setindex(PVector(0.0, 4), 20, 2)
4-element PArray{Float64,1}:
  0.0
 20.0
  0.0
  0.0

julia> nonzeros(u)
1-element PArray{Float64,1}:
 20.0
source
Air.@delay — Macro
@delay expression

Yields a Delay object that matches the given expression. The expression may be one of the following:

  1. A function of no arguments, such as () -> 10; in this case the delay is made from this function (i.e., the RHS is the -> expression that is delayed).
  2. A function with a set of symbol arguments evaluates the RHS but uses a let statement to wrap all the symbols in the LHS into a closure.
  3. An expression, which is treated as equivalent to () -> expression.

Optionally, the expression or LHS may be tagged with a type T. In this case, a Delay{T} object is yielded instead of a Delay{Any}.

See also: Delay, LazyDict

Examples

DocTestSetup = quote
    using Air
end
julia> # Create a Delay with a long run time.
       d = (@delay (println("Running."); sleep(2); 10)::Int64)
Delay{Int64}(<...>)

julia> # Start a few threads, each of which attempt to read it. The function will
       # only run once.
       for th in [(Threads.@spawn d[]) for _ in 1:5]; wait(th) end
Running.

julia> # Ensure that it produced the correct value and doesn't run again.
       d[]
10

julia> # The display now shows the realized value also.
       d
Delay{Int64}(10)

julia> # Create a Delay with a locally-bound symbol.
       d2 = (@delay (d) -> (d[] / 20.0)::Float64)
Delay{Float64}(<...>)

julia> # We can rebind d without affecting d2.
       d = 10
10

julia> d2[]
0.5
source
Air.@memoize — Macro
@memoize name(args...) = expr
@memoize name(args...) where {...} = expr

@memoize is a macro for declaring that the function declaration that follows should be memoized in a private dictionary and any pre-calculated value should be returned from that dictionary instead of being recalculated. All memoization is thread-safe: expr is only ever evaluated by one thread at a time, and is only ever evaluated once per unique set of arguments.

Note that arguments are memoized according to equality, so the use of mutable arguments can result in undefined behavior of those arguments are later changed.

Examples

DocTestSetup = quote
    using Air
end
julia> @memoize fib(n::Int) = begin
           println("Calculating fib($n)...")
           if n < 1
               return 0
           elseif n == 1
               return 1
           else
               return fib(n-1) + fib(n - 2)
           end
       end::Int
fib (generic function with 1 method)

julia> fib(5)
Calculating fib(5)...
Calculating fib(4)...
Calculating fib(3)...
Calculating fib(2)...
Calculating fib(1)...
Calculating fib(0)...
5

julia> fib(6)
Calculating fib(6)...
8
source
Air.@p — Macro
@p{k1 => v1, k2 => v2, ...}
@p[x1, x2, ...]
@p[x1 x2...]
@p(x1, x2, ...)

Yields a persistent data structure, depending on how the macro is called. This is a shorthand for calling the various constructors directly. The following expressions are equivalent:

  • @p{k1 => v1, k2 => v2, ...} and PDict(k1 => v1, k2 => v2, ...)
  • @p[x1, x2, ...] and PVector([x1, x2, ...])
  • @p[x1 x2 ...] and PArray([x1 x2 ...])
  • @p(x1, x2, ...) and PSet([x1, x2, ...]

See also: PDict, PVector, PArray, PSet.

Examples

DocTestSetup = quote
    using Air
end
julia> d = @p{:a => 1, :b => 2, :c => 3};

julia> (d[:a], d[:b], d[:c])
(1, 2, 3)
julia> @p[1, 2, 3, 4]
4-element PArray{Int64,1}:
 1
 2
 3
 4
julia> @p[:q2 :q1; :q3 :q4]
2×2 PArray{Symbol,2}:
 :q2  :q1
 :q3  :q4
julia> s = @p(:a, :b, :a, :c);

julia> (length(s), :a in s, :b in s, :c in s, :d in s)
(3, true, true, true, false)
source
Air.@tx — Macro
@tx expr

The macro @tx should be followed by an expression; that expression is run in an atomic transaction.

source
Air.@var — Macro
@var

Convenient syntax for creating a task-local Var object: @var name = initval will construct a Var object with the given initial value. @var name = initval::T will create a Var{T} object.

See also: Var.

Examples

DocTestSetup = quote
    using Air
end
julia> @var v = :start_sym
Var{Symbol}(@JIm7aUS2sOl: :start_sym; init=:start_sym)

julia> @var u = :start_sym::Any
Var{Any}(@JIm7aUS2sOl: :start_sym; init=:start_sym)
source