You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Roto should have some support for optional/nullable types.
Research
Here is what a bunch of languages do:
Rust: Option<T>
Swift: T? == Optional<T>
Kotlin: T?
Haskell: Maybe T
Go: Uses -1 or nil and all that. nil is just the zero for a bunch of types
Zig: ?T (with null, only 1 layer allowed)
Hare: (T | void)
TypeScript: T? == T | null
Python: Optional[X] == X | None
There's many possible syntaxes, but semantically, it's about how many "layers" of Option are allowed and where optional types are allowed:
It could be an enum: many layers allowed
It could be a type union: 1 layer allowed, so Option<Option<T>> does not exist.
Then there are questions such as:
Do we expose null as a separate type? Or is it just ()?
Do we have type unions in the language?
Can we match on it or should we always use special syntax?
For Roto, the situation is interesting, because most scripting languages opt for a type union.
Proposal
We should probably have T? at least. And then also allow T?? as meaning Option<Option<T>>, because Rust allows that too. It will have to be a separate type from Option though because we need repr(C).
This would mean that you can match on these values like so:
match x {Some(x) => { ...}None => { ...}}
Additionally we could provide the following syntax sugars:
if x { ... } else { ... }
# equivalent to
match x {
Some(x) => { ... }
None => { ... }
}
x or expr
# equivalent to
match x {
Some(x) => x,
None => expr,
}
x?
# equivalent to
match x {
Some(x) => x,
None => return None,
}
try { x?.foo.bar() && baz }
# equivalent to
match x {
Some(x) => x.foo.bar() && baz,
None => None
}
The nice thing about the try block is that multiple ? can be used (just like in functions returning Option in Rust and the block can contain multiple statements.
The main problem lies in expressing Option::map, which is expressible as try { f(x?) } above.
try can also get an else:
try { x } else { 0 }
or that should just be a separate operator or
x or 0
try { x?.foo } or 0
Like with Swift's ??, this operator could take other optional values as well so they can be chained.
It might be possible to omit the {} from the try, but the precedence might get a bit tricky if we do that.
Alternatives
We could go with a map operator instead of the try block.
# similar to `if let Some(x) { x }` in Rust
x map { x.foo }
# or more like Kotlin:
x.try { it.foo }
The downside of try blocks as described above is that nested Options are kind of verbose to deal with. For example, the here is some code in Rust and the equivalent with try blocks:
// This is Rust
x.map(|y| y.map(|y| y.foo));
try {
let y = x?;
try { y?.foo }
}
However, note that matching is still possible:
match x {
Some(Some(x)) => Some(Some(x.foo)),
x => x,
}
We can remedy this a bit with this syntax sugar:
try x { try x { x.foo } }
# desugars to
try { let x = x?; try { let x = x?; x.foo } }
or mixed:
try x { try { x?.foo } }
A more syntax sugary map might look like this:
x map (x map x.foo)
# or with .try
x.try { it.try { it.foo } }
However, flattening Option<Option<T>> to Option<T> is actually very simple with a try block:
try { x?? }
The text was updated successfully, but these errors were encountered:
Roto should have some support for optional/nullable types.
Research
Here is what a bunch of languages do:
Option<T>
T? == Optional<T>
T?
Maybe T
-1
ornil
and all that.nil
is just the zero for a bunch of types?T
(withnull
, only 1 layer allowed)(T | void)
T? == T | null
Optional[X] == X | None
There's many possible syntaxes, but semantically, it's about how many "layers" of
Option
are allowed and where optional types are allowed:Option<Option<T>>
does not exist.Then there are questions such as:
null
as a separate type? Or is it just()
?For Roto, the situation is interesting, because most scripting languages opt for a type union.
Proposal
We should probably have
T?
at least. And then also allowT??
as meaningOption<Option<T>>
, because Rust allows that too. It will have to be a separate type fromOption
though because we needrepr(C)
.This would mean that you can match on these values like so:
Additionally we could provide the following syntax sugars:
The nice thing about the try block is that multiple
?
can be used (just like in functions returningOption
in Rust and the block can contain multiple statements.The main problem lies in expressing
Option::map
, which is expressible astry { f(x?) }
above.try
can also get anelse
:or that should just be a separate operator
or
Like with Swift's
??
, this operator could take other optional values as well so they can be chained.It might be possible to omit the
{}
from thetry
, but the precedence might get a bit tricky if we do that.Alternatives
We could go with a
map
operator instead of thetry
block.The downside of
try
blocks as described above is that nestedOption
s are kind of verbose to deal with. For example, the here is some code in Rust and the equivalent withtry
blocks:However, note that matching is still possible:
We can remedy this a bit with this syntax sugar:
or mixed:
A more syntax sugary
map
might look like this:However, flattening
Option<Option<T>>
toOption<T>
is actually very simple with atry
block:The text was updated successfully, but these errors were encountered: