AL is a live, ACID, (eventually) bitemporal, relational-object operating system built around an append-only command log. It combines inspiration from:
-
XTDB
-
GlamorousToolkit/Pharo
-
Git
-
PROLOG/Logtalk
-
LISP
-
BEAM
-
Urbit
The goal of the system is to merge the ideas of the relational paradigm and of the metaobject protocol, and, as a north star, to be the personal computing environment of the future. This runtime is the prototypical version of AL, written in Elixir. The irony of the first Erlang interpreter having been written in PROLOG is not lost on us.
- Live Smalltalk-style objects, defined relationally. No more faux-ADTs. Define protocols and their implementations. Mix and match at your leisure. With bidirectional method resolution informed by WAM semantics.
- Shutdown your system, continue later. All transactions are backed up by an on-disk database, hydrated at startup.
- ACID transactions ensure your work is safe and easy to reason about.
- CLP over finite domains, including over objects, both durable and ephemeral.
- Git-Like branching behaviour. Fork your system at different points in the system's history.
And to come:
- Bitemporality features: Query objects as of certain times, working with system and business time separately
For discussion of the design philosophy of AL and resources that were consulted during its design, please see: https://forum.anoma.net/t/design-philosophy-of-al-bibliography/2698
Install from terminal using iex -S mix or as a mix dependency.
From IEx, you can run require AL.
lib/examples contains examples.
lib/AL/package contains the bundled packages (the bootstrap package is the foundational one).
lib/AL contains the runtime code.
Look inside a live object.
run do
examine(:object, info)
endExtend an inherited method
run do
defclass :animal, super: :object do
defmethod(:describe, [self, :i_am_animal]) do
end
end
defclass :pet, super: :animal do
defmethod(:describe, [self, d]) do
call_next_method(self, [parent])
unify(d, [:i_am_pet, parent])
end
end
new(:pet, rex)
describe(rex, result)
endRun a method backwards.
run do
factorial(n, 120)
endPropagate constraints about and between objects
run do
defclass :rectangle, super: :value, ivars: [:width, :height, :perimeter] do
defmethod(:init, [self, args, new]) do
slot_get(args, :width, w)
slot_get(args, :height, h)
slot_get(args, :perimeter, p)
eq(p, 2 * w + 2 * h)
unify(new, %{class: :rectangle, width: w, height: h, perimeter: p})
end
defmethod(:get_slot, [self, k, v]) do
vm_map_get(self, k, v)
end
defmethod(:area, [self, result]) do
get_slot(self, :width, w)
get_slot(self, :height, h)
vm_is(result, w * h)
end
end
new(:rectangle, %{width: w, height: 3, perimeter: 16}, r)
area(r, a)
endInfer an object's identity from its class and a slot
run do
defclass :vehicle, super: :object do
end
defclass :car, super: :vehicle do
end
defclass :bicycle, super: :vehicle do
end
defclass :fire_hydrant, super: :object do
end
set_slots(:car, %{color: :red})
set_slots(:bicycle, %{color: :blue})
set_slots(:fire_hydrant, %{color: :red})
new(:car, my_car)
new(:bicycle, my_bike)
new(:fire_hydrant, hydrant)
class(x, :vehicle)
get_slot(x, :color, :red)
endAL persists to a local, gitignored Mnesia store (.mnesiastore/ by default) that every iex -S mix/mix run/mix test invocation in a checkout shares unless told otherwise.
You can utilise forks to have multiple streams work on the same node without conflict.
fork = AL.Branch.fork() <- fork from HEAD
AL.Branch.checkout(fork) <- change HEAD to fork
AL.Branch.discard(fork) <- discard the forkFurther isolation should be accomplished by configuration of the Mnesiastore dir.
Metacello new
repository: 'github://anoma/AL-Ex:main/src';
baseline: 'AL';
loadIf you have an existing bridge with a different version you want to run this without error then run:
Metacello new
repository: 'github://anoma/AL-Ex:main/src';
baseline: 'AL';
load: #dev