# dista.task

> A module of Dista's API.

Tasks: remote functions, their IDs, and how a worker runs one.

A task wraps a top-level `def` with its argument and result types:

```mojo
def square(x: Int) -> Int:
    return x * x

comptime Square = Task[Int, Int, square]
```

The types are spelled out because Mojo can't infer a function's result type
from the function value. The function may raise or not; both bind to
`def(A) thin raises -> R`.

Every node runs the same binary, so a task message names the task by ID: a
64-bit FNV-1a hash of the function's linkage name, which includes its module
and argument types. The hash is fixed, unlike `hash()`, so a driver and a
worker started separately agree on it.

An actor is a state type and the function that builds it, and each of its
methods is a function taking the state `mut`:

```mojo
def new_counter(start: Int) -> Counter:
    return Counter(start)

def add(mut c: Counter, n: Int) -> Int:
    c.total += n
    return c.total

comptime CounterActor = Actor[Counter, Int, new_counter]
comptime Add = Method[CounterActor, Int, Int, add]
```

Actors and methods go in the runtime's registry with the tasks. The worker
hosting an actor keeps its state behind a `StatePtr`, which only the actor's
own `create`, `destroy`, `save` and `restore` and its methods' `call` cast
back to its type. A checkpoint is the state through the codec, so a state
type that implements `Codec` chooses what it saves.

## Structs

### `Task`

```mojo
struct Task[A_: Deinitable & Movable, R_: Deinitable & Movable, f: def(A_) raises thin -> R_]
```

A remote function taking one `A_` and returning an `R_`.

Implements: `Movable`, `TaskLike`

#### `A`

```mojo
comptime A = A_
```

#### `R`

```mojo
comptime R = R_
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `run`

```mojo
def run(args: Span[UInt8], mut out: List[UInt8])
```

### `Task2`

```mojo
struct Task2[A_: Deinitable & Movable, B_: Deinitable & Movable, R_: Deinitable & Movable, f: def(A_, B_) raises thin -> R_]
```

A remote function taking two arguments: `rt.remote[T](a, b)`.

One type per argument count, as Mojo's own library does it until its
variadic type parameters settle, when `Task` can take any number
(https://github.com/magmalake/dista.mojo/issues/71). On the wire the arguments are encoded one after the other.

Implements: `Movable`, `Task2Like`, `TaskLike`

#### `A`

```mojo
comptime A = A_
```

#### `B`

```mojo
comptime B = B_
```

#### `R`

```mojo
comptime R = R_
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `arity`

```mojo
def arity() -> Int
```

#### `run`

```mojo
def run(args: Span[UInt8], mut out: List[UInt8])
```

### `Task3`

```mojo
struct Task3[A_: Deinitable & Movable, B_: Deinitable & Movable, C_: Deinitable & Movable, R_: Deinitable & Movable, f: def(A_, B_, C_) raises thin -> R_]
```

A remote function taking three arguments: `rt.remote[T](a, b, c)`. See `Task2`.

Implements: `Movable`, `Task2Like`, `Task3Like`, `TaskLike`

#### `A`

```mojo
comptime A = A_
```

#### `B`

```mojo
comptime B = B_
```

#### `C`

```mojo
comptime C = C_
```

#### `R`

```mojo
comptime R = R_
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `arity`

```mojo
def arity() -> Int
```

#### `run`

```mojo
def run(args: Span[UInt8], mut out: List[UInt8])
```

### `TaskWith`

```mojo
struct TaskWith[A_: Deinitable & Movable, X: Copyable & Deinitable, R_: Deinitable & Movable, f: def(A_, Seq[X]) raises thin -> R_]
```

A task taking a value and other tasks' results, gathered in order: `f(a, xs)`, submitted as `rt.remote[T](a, refs)`. A shuffle's reducer is one: its partition number, and the map outputs holding that partition.

Implements: `Movable`, `TaskLike`

#### `A`

```mojo
comptime A = A_
```

#### `R`

```mojo
comptime R = R_
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `run`

```mojo
def run(args: Span[UInt8], mut out: List[UInt8])
```

### `TaskMany`

```mojo
struct TaskMany[A_: Deinitable & Movable, X: Copyable & Deinitable, f: def(A_) raises thin -> Seq[X]]
```

A task with several results, each its own object: `rt.remote_many[T](a, n)` returns `n` `ObjectRef[X]`s, and `f` must return a `Seq` of exactly `n`. Each is stored, fetched and freed on its own, so a shuffle's map task can make one partition per reducer.

On the wire its result is a count, then each part as a length and its
encoding, so the worker splits it without knowing `X`.

Implements: `ManyLike`, `Movable`, `TaskLike`

#### `A`

```mojo
comptime A = A_
```

#### `R`

```mojo
comptime R = Seq[X]
```

#### `Part`

```mojo
comptime Part = X
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `is_many`

```mojo
def is_many() -> Bool
```

#### `run`

```mojo
def run(args: Span[UInt8], mut out: List[UInt8])
```

### `Actor`

```mojo
struct Actor[S_: Deinitable & Movable, A_: Deinitable & Movable, new: def(A_) raises thin -> S_]
```

An actor whose state is an `S_`, built by `new` from an `A_`.

Implements: `ActorLike`, `Movable`, `TaskLike`

#### `S`

```mojo
comptime S = S_
```

#### `A`

```mojo
comptime A = A_
```

#### `R`

```mojo
comptime R = NoneType
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `is_actor`

```mojo
def is_actor() -> Bool
```

#### `create`

```mojo
def create(args: Span[UInt8]) -> StatePtr
```

#### `destroy`

```mojo
def destroy(state: Pointer[NoneType, MutUntrackedOrigin])
```

#### `save`

```mojo
def save(state: Pointer[NoneType, MutUntrackedOrigin], mut out: List[UInt8])
```

#### `restore`

```mojo
def restore(bytes: Span[UInt8]) -> StatePtr
```

### `Method`

```mojo
struct Method[Act_: ActorLike, A_: Deinitable & Movable, R_: Deinitable & Movable, f: def(mut Act_.S, A_) raises thin -> R_]
```

A method of actor `Act_`: `f` on its state and an `A_`, returning `R_`.

Implements: `MethodLike`, `Movable`, `TaskLike`

#### `A`

```mojo
comptime A = A_
```

#### `R`

```mojo
comptime R = R_
```

#### `Act`

```mojo
comptime Act = Act_
```

#### `name`

```mojo
def name() -> StaticString
```

#### `id`

```mojo
def id() -> UInt64
```

#### `owner`

```mojo
def owner() -> UInt64
```

#### `call`

```mojo
def call(state: Pointer[NoneType, MutUntrackedOrigin], args: Span[UInt8], mut out: List[UInt8])
```

## Traits

### `TaskLike`

```mojo
undefined
```

What the runtime needs from a task, an actor or a method, without knowing its function. Each implements the part that applies to it.

#### `A`

The argument type.

#### `R`

The result type.

#### `name`

```mojo
def name() -> StaticString
```

The function's linkage name.

#### `id`

```mojo
def id() -> UInt64
```

The task's wire ID.

#### `run`

```mojo
def run(args: Span[UInt8], mut out: List[UInt8])
```

Decodes the argument, calls the function, appends the encoded result.

#### `arity`

```mojo
def arity() -> Int
```

How many arguments it takes: 2 for a `Task2`, 3 for a `Task3`.

#### `is_actor`

```mojo
def is_actor() -> Bool
```

#### `is_many`

```mojo
def is_many() -> Bool
```

Whether its result is several objects (`TaskMany`).

#### `owner`

```mojo
def owner() -> UInt64
```

For a method, its actor's ID; 0 for anything else.

#### `create`

```mojo
def create(args: Span[UInt8]) -> StatePtr
```

An actor: decodes the argument and builds the state.

#### `destroy`

```mojo
def destroy(state: Pointer[NoneType, MutUntrackedOrigin])
```

An actor: destroys the state `create` built.

#### `save`

```mojo
def save(state: Pointer[NoneType, MutUntrackedOrigin], mut out: List[UInt8])
```

An actor: appends its state's encoding, a checkpoint.

#### `restore`

```mojo
def restore(bytes: Span[UInt8]) -> StatePtr
```

An actor: builds the state again from a checkpoint `save` wrote.

#### `call`

```mojo
def call(state: Pointer[NoneType, MutUntrackedOrigin], args: Span[UInt8], mut out: List[UInt8])
```

A method: runs on the actor's state, appends the encoded result.

### `Task2Like`

```mojo
undefined
```

Implements: `TaskLike`

#### `B`

The second argument's type; `A` is the first's.

### `Task3Like`

```mojo
undefined
```

Implements: `Task2Like`, `TaskLike`

#### `C`

The third argument's type.

### `ManyLike`

```mojo
undefined
```

Implements: `TaskLike`

#### `Part`

The type of each of its results.

### `ActorLike`

```mojo
undefined
```

Implements: `TaskLike`

#### `S`

The actor's state type.

### `MethodLike`

```mojo
undefined
```

Implements: `TaskLike`

#### `Act`

The actor whose method this is.

## Functions

### `fnv1a64`

```mojo
def fnv1a64(s: StringSpan) -> UInt64
```

The 64-bit FNV-1a hash of `s`'s bytes.

### `split_parts`

```mojo
def split_parts(result: Span[UInt8]) -> List[List[UInt8]]
```

A `TaskMany` result's parts, each as its own encoded object.

## Aliases

### `StatePtr`

```mojo
comptime StatePtr = Pointer[NoneType, MutUntrackedOrigin]
```

An actor's state on its worker, with its type erased.
