FREAKV3 docs freak 0.14.2 (Maverick)

Tasks#

> Functions are called tasks. One declaration form, one return keyword, no shorthands.

Declaring a task#

FREAK
task name(param: type, param: type) -> returntype {
    body
}

-> returntype may be omitted, in which case the return type is void. Parameter and return types follow the same grammar as everywhere else — one identifier, or List<T>. See the type-annotation grammar.

examples/tasks.fk compilesruns
-- `task` declares a function. `give back` returns a value.
-- A block-bodied task that returns a non-void type must use `give back`
-- on every path; there is no implicit tail return.
task add(a: int, b: int) -> int {
    give back a + b
}

task shout(message: word) -> void {
    say message.to_upper()
}

task classify(n: int) -> word {
    if n < 0 { give back "negative" }
    if n == 0 { give back "zero" }
    give back "positive"
}

task main() -> void {
    say word_from_int(add(20, 22))
    shout("stand by")
    say classify(0 - 3)
    say classify(0)
    say classify(9)
}
Program output
42
STAND BY
negative
zero
positive

give back#

give back expr returns a value. give back on its own returns from a void task.

There is no implicit tail return. A bare expression at the end of a block is evaluated and discarded. Every path that must produce a value needs its own give back:

FREAK
task area(r: num) -> num {
    math::pow(r, 2.0) * 3.14159    -- computed, then thrown away
}

task area_ok(r: num) -> num {
    give back math::pow(r, 2.0) * 3.14159
}

The checker reports a missing return on a value-returning task, pointing at the task keyword of the offending declaration.

Not in V3

The arrow shorthand task square(x: num) => x * x is in the bible but does not parse in V3 — you get expected '{', found '=>'. Neither does the done block terminator: task f() -> intdone fails with expected '{', found 'give back'. Braces are the only block delimiter.

Calling#

FREAK
task main() -> void {
    say word_from_int(add(1, 2))
}

Arguments are positional. Named call arguments — connect(host: "localhost", port: 8080) — are V4 and do not parse.

Argument count and argument types are both checked against the declaration.

Order does not matter#

Every task in the file is indexed before any body is checked, so a task may call one declared later, and may call itself.

examples/recursion.fk compilesruns
-- Tasks may call themselves, and may call tasks declared later in the
-- file: every task is indexed before any body is checked.
task factorial(n: int) -> int {
    if n <= 1 { give back 1 }
    give back n * factorial(n - 1)
}

task fib(n: int) -> int {
    if n < 2 { give back n }
    give back fib(n - 1) + fib(n - 2)
}

task main() -> void {
    say word_from_int(factorial(6))
    say word_from_int(fib(12))
}
Program output
720
144

main#

If a task called main exists it becomes the program entry point. If it does not, top-level statements are the program. You can have both — top-level statements run first, then main.

Parameters are by value#

There is no lend / lend mut in V3: the borrow syntax from the bible does not parse in a parameter list. Every parameter is passed by value.

FREAK
-- does not parse in V3:
--   task show(lend p: Point) -> void { say word_from_int(p.x) }

-- write this instead:
task show(p: Point) -> void { say word_from_int(p.x) }

For shapes this matters: a shape parameter is a handle into runtime storage, so mutating a field inside a task is visible to the caller. Treat shape arguments as shared, and return a new value when you want isolation.

Instance and associated methods#

A task declared inside impl Shape { ... } whose first parameter is self is an instance method; without self it is an associated method. Both are covered in Shapes & impl.

The pipe operator#

|> rewrites a call so the piped value becomes its first argument.

examples/pipe.fk compilesruns
-- `|>` feeds the left value in as the FIRST argument of the call on the
-- right, so `x |> f(y)` is exactly `f(x, y)`.
task double(n: int) -> int { give back n * 2 }
task offset(n: int, by: int) -> int { give back n + by }

task main() -> void {
    say word_from_int(5 |> double())
    say word_from_int(5 |> offset(3))
    say word_from_int(5 |> double() |> offset(1))
}
Program output
10
8
11

x |> f() is exactly f(x), and x |> f(y) is exactly f(x, y). The right side must be a plain task name — you cannot pipe into a method or a shape constructor.

extern tasks#

A task implemented in C is declared with extern, on one line, with no body. See extern & FFI.

FREAK
extern task abs(v: int) -> int

What tasks cannot do#

FeatureStatus
Generic parameters task f<T>(...)V4 — expected '(', found '<'
Named call argumentsV4
Default parameter valuesV4
Variadic parametersV4
Closures / lambdas |x| => x * 2V4 — | cannot start an expression
Arrow bodies => exprV4
done as a block terminatorV4
Returning maybe<T> or result<T,E>V4 — no such types
? error propagation, or elseV4
Visibility modifiers launch / launch(package)V4

Every FREAK snippet on this site was compiled by freak 0.14.2 (Maverick), built from source with a verified self-host fixed point. 45/45 examples compile; regenerate with python tools/verify.py then python tools/build_docs.py.