// learn
KIRun in twelve steps.
Each lesson is a real function. Read it, then open it in the playground and run it. Change the arguments; break it on purpose; switch to the graph.
01 /basics
A function is data
A KIRun function has a name, parameters, events and steps. Each step calls another function. This one prints a greeting and raises its output event with a message: that event is how a function returns.
FUNCTION Hello
NAMESPACE Playground
PARAMETERS
name AS {"type":["STRING"],"defaultValue":"KIRun"}
EVENTS
output
message AS {"type":["STRING"]}
LOGIC
greet: System.Print(values = `'Hello, ' + Arguments.name + '!'`)
done: System.GenerateEvent(eventName = "output", results = {name: "message", value: `'Hello, ' + Arguments.name + '!'`}) AFTER Steps.greet.output
02 /basics
Arguments in, results out
Parameters have schemas, so a and b must be integers before any step runs. Steps.add.output.value reads what the add step returned, and GenerateEvent passes it out as sum.
FUNCTION AddNumbers
NAMESPACE Playground
PARAMETERS
a AS {"type":["INTEGER"]}
b AS {"type":["INTEGER"]}
EVENTS
output
sum AS {"type":["INTEGER"]}
LOGIC
add: System.Math.Add(value = Arguments.a, value = Arguments.b)
done: System.GenerateEvent(eventName = "output", results = {name: "sum", value: Steps.add.output.value})
03 /expressions
Expressions
Values can be expressions: arithmetic, comparison (= for equality), and / or / not, the ternary, ?? for a missing value, and paths with indexes and ranges like Arguments.items[1..3]. A literal string inside a result goes in backticks: `'big'`.
FUNCTION Expressions
NAMESPACE Playground
PARAMETERS
n AS {"type":["INTEGER"]}
items AS {"type":["ARRAY"]}
EVENTS
output
math AS {}
size AS {"type":["STRING"]}
second AS {}
slice AS {}
fallback AS {}
LOGIC
done: System.GenerateEvent(
eventName = "output",
results = {name: "math", value: `Arguments.n * 2 + 1`},
results = {name: "size", value: `Arguments.n > 10 ? 'big' : 'small'`},
results = {name: "second", value: Arguments.items[1]},
results = {name: "slice", value: Arguments.items[1..3]},
results = {name: "fallback", value: `Arguments.missing ?? 'no value'`}
)
04 /events
If raises events
System.If does not return a value: it raises true or false. The steps that say AFTER Steps.check.true run only on true; that set of steps is the branch.
FUNCTION EvenOrOdd
NAMESPACE Playground
PARAMETERS
n AS {"type":["INTEGER"]}
EVENTS
output
parity AS {"type":["STRING"]}
LOGIC
check: System.If(condition = `Arguments.n % 2 = 0`)
true
even: System.GenerateEvent(eventName = "output", results = {name: "parity", value: `'even'`}) AFTER Steps.check.true
false
odd: System.GenerateEvent(eventName = "output", results = {name: "parity", value: `'odd'`}) AFTER Steps.check.false
05 /events
Several outcomes
A function can declare more than one event. SafeDivide ends in output with a quotient, or in error with a message. Run it with b = 0.
FUNCTION SafeDivide
NAMESPACE Playground
PARAMETERS
a AS {"type":["DOUBLE"]}
b AS {"type":["DOUBLE"]}
EVENTS
output
quotient AS {"type":["DOUBLE"]}
error
message AS {"type":["STRING"]}
LOGIC
zero: System.If(condition = `Arguments.b = 0`)
true
fail: System.GenerateEvent(eventName = "error", results = {name: "message", value: `'Cannot divide by zero'`}) AFTER Steps.zero.true
false
ok: System.GenerateEvent(eventName = "output", results = {name: "quotient", value: `Arguments.a / Arguments.b`}) AFTER Steps.zero.false
06 /events
Indentation is not a dependency
Writing a step inside the true block does not make it wait for true. Only AFTER or a Steps. reference creates a dependency. Run with go = false: the indented step that has no AFTER still runs.
FUNCTION IndentationIsNotADependency
NAMESPACE Playground
PARAMETERS
go AS {"type":["BOOLEAN"]}
LOGIC
check: System.If(condition = Arguments.go)
true
follows: System.Print(values = "runs only when go is true: it says AFTER Steps.check.true") AFTER Steps.check.true
alwaysRuns: System.Print(values = "runs every time: indented, but nothing makes it wait for the true event")
07 /loops
Loops
RangeLoop raises iteration once per index, then output when it is done. The steps that wait for iteration run each time; a Context value carries the running total between them.
FUNCTION SumOneToN
NAMESPACE Playground
PARAMETERS
n AS {"type":["INTEGER"]}
EVENTS
output
total AS {"type":["INTEGER"]}
LOGIC
create: System.Context.Create(name = "total", schema = {type: "INTEGER"})
start: System.Context.Set(name = "Context.total", value = 0) AFTER Steps.create.output
loop: System.Loop.RangeLoop(from = 1, to = `Arguments.n + 1`) AFTER Steps.start.output
iteration
add: System.Context.Set(name = "Context.total", value = `Context.total + Steps.loop.iteration.index`) AFTER Steps.loop.iteration
done: System.GenerateEvent(eventName = "output", results = {name: "total", value: Context.total}) AFTER Steps.loop.output
08 /loops
Break
ForEachLoop walks an array. System.Loop.Break, given the loop's step name, ends it early: the words after stop are never printed.
FUNCTION UntilStop
NAMESPACE Playground
PARAMETERS
words AS {"type":["ARRAY"]}
LOGIC
loop: System.Loop.ForEachLoop(source = Arguments.words)
iteration
stop: System.If(condition = `Steps.loop.iteration.each = 'stop'`) AFTER Steps.loop.iteration
true
leave: System.Loop.Break(stepName = "loop") AFTER Steps.stop.true
false
say: System.Print(values = Steps.loop.iteration.each) AFTER Steps.stop.false
09 /parallel
Steps run in parallel
Two steps that do not wait on each other start together. Two 300 ms waits finish in about 300 ms; the status bar shows the time and the plan view shows both in the same level.
FUNCTION TwoAtOnce
NAMESPACE Playground
EVENTS
output
done AS {"type":["STRING"]}
LOGIC
left: System.Wait(millis = 300)
right: System.Wait(millis = 300)
both: System.GenerateEvent(eventName = "output", results = {name: "done", value: `'both waits finished'`}) AFTER Steps.left.output, Steps.right.output
10 /loops
Context
Context values live for one run of the function. Create declares one with a schema, Set changes it, and any expression reads it as Context.a. Try n = 50: the values are LONG.
FUNCTION Fibonacci
NAMESPACE Playground
PARAMETERS
n AS {"type":["INTEGER"]}
EVENTS
output
value AS {"type":["LONG"]}
LOGIC
makeA: System.Context.Create(name = "a", schema = {type: "LONG"})
makeB: System.Context.Create(name = "b", schema = {type: "LONG"})
setA: System.Context.Set(name = "Context.a", value = 0) AFTER Steps.makeA.output
setB: System.Context.Set(name = "Context.b", value = 1) AFTER Steps.makeB.output
loop: System.Loop.CountLoop(count = Arguments.n) AFTER Steps.setA.output, Steps.setB.output
iteration
next: System.Context.Set(name = "Context.b", value = `Context.a + Context.b`) AFTER Steps.loop.iteration
shift: System.Context.Set(name = "Context.a", value = `Context.b - Context.a`) AFTER Steps.next.output
done: System.GenerateEvent(eventName = "output", results = {name: "value", value: Context.a}) AFTER Steps.loop.output
11 /schemas
Schemas
ValidateSchema checks any value against a JSON-style schema: types, required properties, minLength, minimum and more. The same checks guard every parameter before a step runs.
FUNCTION CheckUser
NAMESPACE Playground
PARAMETERS
user AS {}
EVENTS
output
valid AS {"type":["BOOLEAN"]}
LOGIC
check: System.ValidateSchema(
source = Arguments.user,
schema = {type: "OBJECT", properties: {name: {type: "STRING", minLength: 2}, age: {type: "INTEGER", minimum: 0}}, required: ["name"]}
)
done: System.GenerateEvent(eventName = "output", results = {name: "valid", value: Steps.check.output.isValid})
12 /dsl
Named values
A big object can be written once under VALUES and used as @shape. System.Make builds a value from that shape and fills its {{ }} placeholders from the arguments.
FUNCTION Settings
NAMESPACE Playground
PARAMETERS
user AS {"type":["STRING"]}
EVENTS
output
settings AS {}
LOGIC
make: System.Make(resultShape = @shape)
done: System.GenerateEvent(eventName = "output", results = {name: "settings", value: Steps.make.output.value})
VALUES
@shape = {
owner: "{{Arguments.user}}",
theme: "dark",
editor: {font: "JetBrains Mono", tabSize: 4, wordWrap: true},
runtimes: ["js", "java", "rust", "python"]
}
Now write your own.