# Writing Built in Functions
As mentioned in the introduction you can extend Graphix by writing
built in functions in rust. This chapter will deep dive into the full
API, if you just want to write a pure function see the
[Overview](./overview.md).
In order to implement a built-in graphix function you must implement
two traits,
[`graphix_compiler::BuiltIn`](https://docs.rs/graphix-compiler/latest/graphix_compiler/trait.BuiltIn.html)
and
[`graphix_compiler::Apply`](https://docs.rs/graphix-compiler/latest/graphix_compiler/trait.Apply.html). See
the rustdoc for details. These two traits give you more control than the
[`graphix_stdlib::CachedArgs`](https://docs.rs/graphix-stdlib/latest/graphix_stdlib/struct.CachedArgs.html) method we covered in the overview. Lets look at the simplest possible example,
## Understanding The Once Function
The `once` function evaluates it's arugment every cycle and passes
through one and only one update. The `update` method is the most
important method of `Apply`, it is called every cycle and returns
something only when the node being updated has "updated". The meaning
of that is specific to what the node does, but in the case of `once`
it means that the argument to `once` updated, and `once` has not
already seen an update. Consider the example program,
```graphix
let clock = time::timer(1, true);
println(once(clock))
```
We expect this example to print the datetime exactly one time. Lets
dig in to how that actually works. The clock created by `time::timer`
will tick once per second forever. The `time::timer` built-in will
call `set_timer` in the
[`Rt`](https://docs.rs/graphix-compiler/latest/graphix_compiler/trait.Rt.html),
which is part of the
[`ExecCtx`](https://docs.rs/graphix-compiler/latest/graphix_compiler/struct.ExecCtx.html). This
will schedule a cycle to happen 1 second from now, and will also
register that this toplevel node (`let clock = ...`) depends on the
timer event. When the timer event happens the approximate sequence of
events is,
- let clock = time::timer(1, true), update called on toplevel node (Bind)
- time::timer(1, true), bind calls update on it's rhs
- time::timer checks events to see if it should update, returns Some(DateTime(..))
- bind sets the id of clock in events to Value::DateTime(..)
- Rt checks for nodes that depend on `clock` schedules println(..)
- println(once(clock)), update called on toplevel node (CallSite)
- once(clock), println calls update on it's argument, once(clock)
- once::update calls update on clock
- ref clock checks events to see if it updated, returns Some(Value::DateTime(..))
- once::update checks if it's the first time it's argument has updated, it is
- once::update returns Some(Value::DateTime(..))
- println prints the datetime
## Implementing Once
```rust
#[derive(Debug)]
struct Once {
val: bool,
}
impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Once {
const NAME: &str = "once";
deftype!("core", "fn('a) -> 'a");
fn init(_: &mut ExecCtx<R, E>) -> BuiltInInitFn<R, E> {
Arc::new(|_, _, _, _, _| Ok(Box::new(Once { val: false })))
}
}
impl<R: Rt, E: UserEvent> Apply<R, E> for Once {
fn update(
&mut self,
ctx: &mut ExecCtx<R, E>,
from: &mut [Node<R, E>],
event: &mut Event<E>,
) -> Option<Value> {
match from {
[s] => s.update(ctx, event).and_then(|v| {
if self.val {
None
} else {
self.val = true;
Some(v)
}
}),
_ => None,
}
}
fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
self.val = false
}
}
```
The BuiltIn trait is for construction, it declares the built in's
name, and it's type (which we get to write out in syntax due to the
deftype macro). It's init method is called every time the built in
needs to be registered with a new context, and returns a closure that
allocates the initial state of the function given a bunch of
information about the context it's called in. In this case we don't
care about any of that information, but it will be useful later.
The most important method of `Apply` is update. `sleep` is expected to
reset all the internal state and unregister anything registered with
the context.
## Higher Order Functions
Right, now that the easy stuff is out of the way, lets see how we can
implement a built-in that takes another Graphix function as an
argument. This gets compiler guts all over the place, sorry about
that. Again lets look at the simplest example from the standard
library, which is `array::group`.
```rust
#[derive(Debug)]
pub(super) struct Group<R: Rt, E: UserEvent> {
queue: VecDeque<Value>,
buf: SmallVec<[Value; 16]>,
pred: Node<R, E>,
ready: bool,
pid: BindId,
nid: BindId,
xid: BindId,
}
impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Group<R, E> {
const NAME: &str = "array_group";
deftype!(
"core::array",
"fn('a, fn(i64, 'a) -> bool throws 'e) -> Array<'a> throws 'e"
);
fn init(_: &mut ExecCtx<R, E>) -> BuiltInInitFn<R, E> {
Arc::new(|ctx, typ, scope, from, top_id| match from {
[_, _] => {
let scope =
scope.append(&format_compact!("fn{}", LambdaId::new().inner()));
let n_typ = Type::Primitive(Typ::I64.into());
let etyp = typ.args[0].typ.clone();
let mftyp = match &typ.args[1].typ {
Type::Fn(ft) => ft.clone(),
t => bail!("expected function not {t}"),
};
let (nid, n) =
genn::bind(ctx, &scope.lexical, "n", n_typ.clone(), top_id);
let (xid, x) = genn::bind(ctx, &scope.lexical, "x", etyp.clone(), top_id);
let pid = BindId::new();
let fnode = genn::reference(ctx, pid, Type::Fn(mftyp.clone()), top_id);
let pred = genn::apply(fnode, scope, vec![n, x], &mftyp, top_id);
Ok(Box::new(Self {
queue: VecDeque::new(),
buf: smallvec![],
pred,
ready: true,
pid,
nid,
xid,
}))
}
_ => bail!("expected two arguments"),
})
}
}
impl<R: Rt, E: UserEvent> Apply<R, E> for Group<R, E> {
fn update(
&mut self,
ctx: &mut ExecCtx<R, E>,
from: &mut [Node<R, E>],
event: &mut Event<E>,
) -> Option<Value> {
macro_rules! set {
($v:expr) => {{
self.ready = false;
self.buf.push($v.clone());
let len = Value::I64(self.buf.len() as i64);
ctx.cached.insert(self.nid, len.clone());
event.variables.insert(self.nid, len);
ctx.cached.insert(self.xid, $v.clone());
event.variables.insert(self.xid, $v);
}};
}
if let Some(v) = from[0].update(ctx, event) {
self.queue.push_back(v);
}
if let Some(v) = from[1].update(ctx, event) {
ctx.cached.insert(self.pid, v.clone());
event.variables.insert(self.pid, v);
}
if self.ready && self.queue.len() > 0 {
let v = self.queue.pop_front().unwrap();
set!(v);
}
loop {
match self.pred.update(ctx, event) {
None => break None,
Some(v) => {
self.ready = true;
match v {
Value::Bool(true) => {
break Some(Value::Array(ValArray::from_iter_exact(
self.buf.drain(..),
)))
}
_ => match self.queue.pop_front() {
None => break None,
Some(v) => set!(v),
},
}
}
}
}
}
fn typecheck(
&mut self,
ctx: &mut ExecCtx<R, E>,
_from: &mut [Node<R, E>],
) -> anyhow::Result<()> {
self.pred.typecheck(ctx)
}
fn refs(&self, refs: &mut Refs) {
self.pred.refs(refs)
}
fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
ctx.cached.remove(&self.nid);
ctx.cached.remove(&self.pid);
ctx.cached.remove(&self.xid);
self.pred.delete(ctx);
}
fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
self.pred.sleep(ctx);
}
}
```
This implements `array::group`, which given an argument, stores that
argument's updates internally, and creates an array out of them when
the predicate returns true. It's type is
```fn('a, fn(i64, 'a) -> bool) -> Array<'a>```
For example,
```graphix
let n = seq(0, 100);