Let’s start by looking at an example of Unit.
Writing a file gives the caller no file contents back, but the write can still succeed or fail. Panackelty represents both outcomes with a Result. The fs_write function returns Result[Unit,HostError].
A Result describes both outcomes
Result has two type parameters: the first describes the value inside Ok(...), and the second describes the value inside Error(...). A function returning Result produces one value, which is always one of those cases:
Ok(())means the write succeeded.()is aUnitvalue, so there is no extra data to return.Error(problem)means the write failed.problemhas typeHostError.
Result[HostError] is not a form Panackelty uses. It would name only the error value and leave the success value unspecified. For fs_write, success carries Unit and failure carries HostError.
For a smaller example, this function returns the same shape with Str as its error type:
pure finish_step(completed: Bool): Result[Unit,Str] {
if completed {
Ok(())
} else {
Error("step failed")
}
}
Here, Ok(()) is success and Error("step failed") is failure. The caller can handle either case explicitly.
Why is the success type Unit?
The success case still needs a value type, even when success has no data to return. Unit has one value, written (). That gives Result a real value for Ok(()) without inventing a dummy answer.
By comparison, fs_read returns Result[Bytes,HostError] because a successful read does have data: the bytes it read. Both functions report errors in the same way; the first type parameter records what success carries.
Unit is not Void
Unit and Void can look similar because both are associated with functions that may not return useful data. They have different roles:
Unitis a first-class value. It can be stored, passed, returned and used insideResult, records, enums and collections.Voidis a function return marker. It is not a value that can be stored or passed around.
That means Void cannot be used as the success type in Result: Result[Void,E] is not a valid Panackelty type. Ok(...) carries a value, and Void is not a value. Unit supplies the value for a successful operation with no data: Ok(()).
A Void function may reach the end of its body without producing a value. A function declared to return Unit must explicitly produce ():
main(): Void {
print("Finished")
}
pure finish(): Unit {
()
}
The empty parentheses on the last line are the result. They are not an omitted result.
Unit is also different from None(). None() means an optional value is absent; Unit is a present success value with nothing more to carry. Use Void when a function returns no value at all. Use Unit when the result needs to say that an operation succeeded, even though there is no data to return.
For the exact rules and more examples, see First-class Unit in the specification, Exact fractions and Unit in the README, and typed filesystem operations.
Is this hugely useful? Necessary? Your mileage may vary, and that’s fine. I think it’s a perfect fit for Panackelty.