Optional
is a great way to achieve dynamism at runtime. However, often the existence of attributes/arguments can be already inferred at comptime.
Consider a function that takes an optional argument, which is only processed if provided:
fn fun(
owned arg1: Float64, arg2: Float64, optional_arg: Optional[Float64] = None
) -> Float64:
if Bool(optional_arg):
arg1 *= optional_arg.value()
return arg1 + arg2
This works, but requires a runtime if evaluation and reserves memory for the optional_arg
even if not needed.
A more efficient solution would be an overload:
fn fun(arg1: Float64, arg2: Float64) -> Float64:
return arg1 + arg2
fn fun(owned arg1: Float64, arg2: Float64, optional_arg: Float64) -> Float64:
arg1 *= optional_arg
return arg1 + arg2
However, this is verbose and can lead to duplicate code. I would like to propose (and beforehand get some feedback) to add a static optional type, which would enable the following features:
- Zero-cost optional arguments
- Automatic inference of the presence of the optional at comptime
- Parametric existence of struct attributes.
The above example would then read as follows:
fn fun[
has_optional: Bool = False
](
owned arg1: Float64,
arg2: Float64,
optional_arg: ComptimeOptional[Float64, has_optional] = None,
) -> Float64:
@parameter
if Bool(optional_arg):
arg1 *= optional_arg.value()
return arg1 + arg2
Because I needed this type for Larecs, I have already implemented a version there. As another showcase for the gained ergonomics, consider the following example:
from larecs.comptime_optional import ComptimeOptional
from sys.info import sizeof
struct S[has_value: Bool = False]:
var _value: ComptimeOptional[Int, has_value]
fn __init__(out self, value: ComptimeOptional[Int, has_value] = None):
self._value = value
def main():
s0 = S()
s1 = S(1)
print("Size of s0 =", sizeof[__type_of(s0)]())
print("Size of s1 =", sizeof[__type_of(s1)]())
@parameter
if s0.has_value:
print(s0._value.value())
@parameter
if s1.has_value:
print(s1._value.value())
Output:
Size of s0 = 0
Size of s1 = 8
1
I’d be very open to renaming the type to StaticOptional
(or any other more suitable name) and happy to create a proposal on GH and a corresponding PR based on my implementation in Larecs (some adjustment might be necessary to mirror the behaviour of the builtin Optional
). However, I want to keep the repo clean of unwanted feature requests of additional types.
What do people think?