Atom

type Atom<T> @abstract =
    var value : T
    let callbacks = StdList.new

    def subscribe (f : T * T -> Unit) =
        callbacks.add f
        Token { callbacks.remove f }

Атом это контейнер, хранящий значение и оповещающий о его изменениях.

type MutAtom<T> @[mut_of Atom] =
    inherit Atom<T>

    def set (x : T) =
        if x == value then return

        let prev_x = value
        value = x

        for f in callbacks do
            f prev_x x

MutAtom это изменяемая версия типа Atom. Имена MutAtom и mut Atom являются синонимами.

type Token =
    let f : Unit -> Unit
    let mut is_discarded = false

    def discard =
        assert not is_discarded
        is_discarded = true
        f ()

Метод subscribe возвращает объект типа Token, позволяющий отменить подписку.

var obs position = Vector3.new
var obs origin = Vector3.new

val global_position@obs = origin@obs + position@obs

let token = position@atom
    |> subscribe { prev_v v -> println "position changed from $prev_v to $v" }

Для привязок position и origin создаются объекты MutAtom<Vector3>. Значение global_position будет пересчитано при каждом их изменении.

Модификатор @atom возвращает контейнер. Фигурные скобки определяют замыкание.

let tuple_atom = (position@atom, origin@atom) |> to_atom

let sum@atom = tuple_atom.map { u, v -> u + v }

to_atom преобразовывает кортеж атомов в атом кортежей. Привязка tuple_atom имеет тип Atom<(Vector3, Vector3)>.

Метод map типа Atom получает в качестве аргумента замыкание типа (Vector3, Vector3) -> Vector3 и возвращает объект Atom<Vector3>. Модификатор @atom на привязке sum скрывает контейнер. Доступ к sum будет возвращать значение поля value контейнера, а присваивание вызовет метод set.

Запятая между u и v в списке параметров замыкания означает, что это элементы кортежа. Скобки не обязательны, если кортеж является единственным параметром.