Package org.kink_lang.kink.hostfun
Host funs can be built using HostFunBuilder, through vm.fun.make() or vm.fun.make(desc).
The following example makes a host fun which compares two nums then returns the smaller one.
FunVal min = vm.fun.make("min(x y)").take(2).action(c -> { Val x = c.arg(0); Val y = c.arg(1); if (! (x instanceof NumVal && y instanceof NumVal)) { return c.raise("max(x y): required two nums, but were not"); } BigDecimal xn = ((NumVal) x).bigDecimal(); BigDecimal yn = ((NumVal) y).bigDecimal(); return xn.compareTo(yn) <= 0 ? x : y; });
If the number of arguments passed to the fun is not two,
the fun raises a Kink exception, because
take(2)
is specified.
After the arity checking, the control is delegated to the lambda given to
action
method.
The following is a quick guidance to compose a host fun:
Take a recv and args
You can get the recv passed to the fun call using CallContext.recv()
.
You can get the number of args using CallContext.argCount()
,
and get an arg using CallContext.arg(int)
.
Return a val
If you want the fun to return a val, just return the val as the result of the lambda, as in the example program above.
Raise an exception
If you want to raise an exception, retrun the result of
HostContext.raise(String)
or HostContext.raise(Throwable)
as in the example program above.
Call a fun
You can call a fun as both a tail call and a non-tail call.
If you want to call a fun as a tail call, use flowish API starting from HostContext.call methods without terminating by CallFlowToOn.on(HostFunReaction).
Example:
int exitHandle = vm.sym.handleFor("exit"); FunVal abort = vm.fun.make().action(c -> { return c.call("kink/PROCESS", exitHandle).args(vm.num.of(1)); });
If you want to call a fun as a non-tail call, use flowish API starting from HostContext.call methods terminating by CallFlowToOn.on(HostFunReaction).
Example:
int opLtHandle = vm.sym.handleFor("op_lt"); FunVal min = vm.fun.make("min(x y)").take(2).action(c -> { Val x = c.arg(0); Val y = c.arg(1); return c.call(y, opLtHandle).args(x).on((cc, yIsSmaller) -> { // yIsSmaller is the result of y.op_lt(x) return yIsSmaller.equals(vm.bool.trueVal) ? y : yIsSmaller.equals(vm.bool.falseVal) ? x : cc.raise("min(x y): got non-bool result from < operator"); }); });
CallContext
given to the action
lambda (c
in the program above)
is valid until the lambda returns the result.
Thus, in the on
lambda, HostContext
given to the on
lambda
(cc
in the program above) must be used instead of c
.
Note: the Kink runtime manages its own runtime.
Thus, an action fun returns a HostResult to the Kink runtime to let it call a Kink fun,
instead of calling the Kink fun on the Java stack frame of the action
lambda itself.
This kind of technique is called
“trampoline.”
Call a graph
Trampoline code described in the previous section can become messy easily,
when calling multiple functions.
In this case, you can use Graph DSL
.
Graph DSL abstracts out a chain of CallFlowToOn.on(ThrowingFunction2)
.
Example:
FunVal min = vm.fun.make("min(x y)").take(2).action(c -> { Val x = c.arg(0); Val y = c.arg(1); if (! (x instanceof NumVal && y instanceof NumVal)) { // call a graph node return c.call(vm.graph.raiseFormat( "min(x y): required two nums, but got {} and {}", vm.graph.repr(x), vm.graph.repr(y))); } BigDecimal xn = ((NumVal) x).bigDecimal(); BigDecimal yn = ((NumVal) y).bigDecimal(); return xn.compareTo(yn) <= 0 ? x : y; });
-
ClassDescriptionContext of a call of a host fun.Call flow step to bind args.Call flow step to bind a return val handler.Call flow step to bind a recv.Context of an action of a host fun.Action of a host fun.An immutable buildre of host funs.The reaction of a host fun on the result of invocation.Result of a host call.