Extension of the Elixir standard library focused on data stuctures, data manipulation and performance
Elixir
226
353 commits
updated Oct 19, 2025
Extension of the Elixir standard library focused on data stuctures, data manipulation and performance.
"there is one aspect of functional programming that no amount of cleverness on the part of the compiler writer is likely to mitigate — the use of inferior or inappropriate data structures." -- Chris Okasaki
Aja.VectorA blazing fast, pure Elixir implementation of a persistent vector, meant to offer an efficient alternative to lists. Supports many operations like appends and random access in effective constant time.
iex> vector = Aja.Vector.new(1..10)
vec([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
iex> Aja.Vector.append(vector, :foo)
vec([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, :foo])
iex> vector[3]
4
iex> Aja.Vector.replace_at(vector, -1, :bar)
vec([1, 2, 3, 4, 5, 6, 7, 8, 9, :bar])
iex> 3 in vector
true
Aja.Vector reimplements many of the functions from the Enum module
specifically for vectors, with efficiency in mind. It should be easier to use
from Elixir than Erlang's :array
module and faster in most cases.
The Aja.vec/1 and Aja.vec_size/1 macros, while being totally optional, can
make it easier to work with vectors and make pattern-matching possible:
iex> import Aja
iex> vec([a, 2, c, _d, e]) = Aja.Vector.new(1..5); {a, c, e}
{1, 3, 5}
iex> vec(first ||| last) = Aja.Vector.new(1..1_000_000); {first, last}
{1, 1000000}
iex> match?(v when vec_size(v) > 9, vec(1..10))
true
The Aja.+++/2 operator provides synctactic sugar for vector concatenation:
iex> vec([1, 2, 3]) +++ vec([4, 5])
vec([1, 2, 3, 4, 5])
Aja.OrdMapThe standard library does not offer any similar functionality:
iex> %{"one" => 1, "two" => 2, "three" => 3}
%{"one" => 1, "three" => 3, "two" => 2}
iex> ord_map = Aja.OrdMap.new([{"one", 1}, {"two", 2}, {"three", 3}])
ord(%{"one" => 1, "two" => 2, "three" => 3})
iex> ord_map["two"]
2
iex> Enum.to_list(ord_map)
[{"one", 1}, {"two", 2}, {"three", 3}]
Ordered maps behave pretty much like regular maps, and the Aja.OrdMap module
offers the same API as Map. The convenience macro Aja.ord/1 make them a
breeze to instantiate or pattern-match upon:
iex> import Aja
iex> ord_map = ord(%{"一" => 1, "二" => 2, "三" => 3})
ord(%{"一" => 1, "二" => 2, "三" => 3})
iex> ord(%{"三" => three, "一" => one}) = ord_map
iex> {one, three}
{1, 3}
All data structures offer:
Inspect, Enumerable and Collectable protocolsAccess behaviourJSON.Encoder protocol (on Elixir 1.18+)Jason is installed) implemention of the Jason.Encoder
protocolEnum: Aja.EnumAja.Enum mirrors the Enum module, but its implementation is highly optimized
for Aja structures such as Aja.Vector or Aja.OrdMap.
Aja.Enum on vectors/ord maps can often be faster than Enum on lists/maps,
depending on the function and size of the sequence.
Aja can be installed by adding aja to your list of dependencies in mix.exs:
def deps do
[
{:aja, "~> 0.7.4"}
]
end
Or you can just try it out from iex or an .exs script:
iex> Mix.install([:aja])
:ok
iex> Aja.Vector.new(["Hello", "world!"])
vec(["Hello", "world!"])
Documentation can be found at https://hexdocs.pm/aja.
(* while fast compile time is a target, vectors are optimized for fast runtime at the expense of compile time)
Aja is still pretty early stage and the high-level organisation is still in flux. Expect some breaking changes until it reaches maturity.
However, most of its APIs are based on the standard library and should therefore remain fairly stable.
Besides, Aja is tested quite thoroughly both with unit tests and property-based testing (especially for data structures). This effort is far from perfect, but increases our confidence in the overall reliability.
Most operations from Aja.Vector are much faster than Erlang's :array
equivalents, and in some cases are even noticeably faster than equivalent list
operations (map, folds, join, sum...). Make sure to read the efficiency guide
from Aja.Vector doc.
Performance for ordered maps has an inevitable though decent overhead over plain
maps in terms of creation and update time (write operations), as well as memory
usage, since some extra work is needed to keep track of the order. It has
however very good read performance, with a very minimal overhead in terms of key
access, and can be enumerated much faster than maps using Aja.Enum.
Aja's data structures (vectors and ordered maps) are already pretty fast on
pre-JIT versions of OTP (<= 23). Benchmarks on OTP 24 suggest however that
they are taking great advantage of the
JIT, relative to lists/maps,
making them even more interesting performance-wise.
Aja data structures should work fine in most cases, but if you're considering them for performance-critical sections of your code, make sure to benchmark them.
Benchmarking is still a work in progress, but you can check the
bench folder for more
detailed figures.
Aja is licensed under the MIT License.
Elixir
100.0%
Extension of the Elixir standard library focused on data stuctures, data manipulation and performance
Elixir
226
353 commits
updated Oct 19, 2025
Extension of the Elixir standard library focused on data stuctures, data manipulation and performance.
"there is one aspect of functional programming that no amount of cleverness on the part of the compiler writer is likely to mitigate — the use of inferior or inappropriate data structures." -- Chris Okasaki
Aja.VectorA blazing fast, pure Elixir implementation of a persistent vector, meant to offer an efficient alternative to lists. Supports many operations like appends and random access in effective constant time.
iex> vector = Aja.Vector.new(1..10)
vec([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
iex> Aja.Vector.append(vector, :foo)
vec([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, :foo])
iex> vector[3]
4
iex> Aja.Vector.replace_at(vector, -1, :bar)
vec([1, 2, 3, 4, 5, 6, 7, 8, 9, :bar])
iex> 3 in vector
true
Aja.Vector reimplements many of the functions from the Enum module
specifically for vectors, with efficiency in mind. It should be easier to use
from Elixir than Erlang's :array
module and faster in most cases.
The Aja.vec/1 and Aja.vec_size/1 macros, while being totally optional, can
make it easier to work with vectors and make pattern-matching possible:
iex> import Aja
iex> vec([a, 2, c, _d, e]) = Aja.Vector.new(1..5); {a, c, e}
{1, 3, 5}
iex> vec(first ||| last) = Aja.Vector.new(1..1_000_000); {first, last}
{1, 1000000}
iex> match?(v when vec_size(v) > 9, vec(1..10))
true
The Aja.+++/2 operator provides synctactic sugar for vector concatenation:
iex> vec([1, 2, 3]) +++ vec([4, 5])
vec([1, 2, 3, 4, 5])
Aja.OrdMapThe standard library does not offer any similar functionality:
iex> %{"one" => 1, "two" => 2, "three" => 3}
%{"one" => 1, "three" => 3, "two" => 2}
iex> ord_map = Aja.OrdMap.new([{"one", 1}, {"two", 2}, {"three", 3}])
ord(%{"one" => 1, "two" => 2, "three" => 3})
iex> ord_map["two"]
2
iex> Enum.to_list(ord_map)
[{"one", 1}, {"two", 2}, {"three", 3}]
Ordered maps behave pretty much like regular maps, and the Aja.OrdMap module
offers the same API as Map. The convenience macro Aja.ord/1 make them a
breeze to instantiate or pattern-match upon:
iex> import Aja
iex> ord_map = ord(%{"一" => 1, "二" => 2, "三" => 3})
ord(%{"一" => 1, "二" => 2, "三" => 3})
iex> ord(%{"三" => three, "一" => one}) = ord_map
iex> {one, three}
{1, 3}
All data structures offer:
Inspect, Enumerable and Collectable protocolsAccess behaviourJSON.Encoder protocol (on Elixir 1.18+)Jason is installed) implemention of the Jason.Encoder
protocolEnum: Aja.EnumAja.Enum mirrors the Enum module, but its implementation is highly optimized
for Aja structures such as Aja.Vector or Aja.OrdMap.
Aja.Enum on vectors/ord maps can often be faster than Enum on lists/maps,
depending on the function and size of the sequence.
Aja can be installed by adding aja to your list of dependencies in mix.exs:
def deps do
[
{:aja, "~> 0.7.4"}
]
end
Or you can just try it out from iex or an .exs script:
iex> Mix.install([:aja])
:ok
iex> Aja.Vector.new(["Hello", "world!"])
vec(["Hello", "world!"])
Documentation can be found at https://hexdocs.pm/aja.
(* while fast compile time is a target, vectors are optimized for fast runtime at the expense of compile time)
Aja is still pretty early stage and the high-level organisation is still in flux. Expect some breaking changes until it reaches maturity.
However, most of its APIs are based on the standard library and should therefore remain fairly stable.
Besides, Aja is tested quite thoroughly both with unit tests and property-based testing (especially for data structures). This effort is far from perfect, but increases our confidence in the overall reliability.
Most operations from Aja.Vector are much faster than Erlang's :array
equivalents, and in some cases are even noticeably faster than equivalent list
operations (map, folds, join, sum...). Make sure to read the efficiency guide
from Aja.Vector doc.
Performance for ordered maps has an inevitable though decent overhead over plain
maps in terms of creation and update time (write operations), as well as memory
usage, since some extra work is needed to keep track of the order. It has
however very good read performance, with a very minimal overhead in terms of key
access, and can be enumerated much faster than maps using Aja.Enum.
Aja's data structures (vectors and ordered maps) are already pretty fast on
pre-JIT versions of OTP (<= 23). Benchmarks on OTP 24 suggest however that
they are taking great advantage of the
JIT, relative to lists/maps,
making them even more interesting performance-wise.
Aja data structures should work fine in most cases, but if you're considering them for performance-critical sections of your code, make sure to benchmark them.
Benchmarking is still a work in progress, but you can check the
bench folder for more
detailed figures.
Aja is licensed under the MIT License.
Elixir
100.0%