-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Consider a tuple
bing : (foo: str, bar: i32)Even though it contains a str, bing cannot be used as one without .foo.
However, it is sometimes desirable to allow a tuple to be used as one of its members, without having to add the explicit access.
This could be done with the addition of a new parameter modifier this. Initially this could be implemented as an attribute #this on parameters. When this is present on a parameter list q : (this X, ...Y), it means that q can be used in place of something expecting X.
Additionally, a second modifier infer could be added which infers the requested parameter from context (or at least attempting to) when the tuple is created. Similarly this can be also done through an attribute #infer. In some ways, infer is the inverse of this: this is relevant for tuple destruction (on field access, ignoring the other fields), while infer is relevant for tuple construction (inferring the other fields). It is useful when you have a x: X and need to fulfil the type (this X, infer Y): instead of providing (x, get_y_somehow()) you could provide x and expect that due to the presence of infer, get_y_somehow() will be inferred. Of course, the inference is a best-effort, and if it fails the compiler will request the parameter to be added manually.
Why is any of this needed or useful? See the use cases below.
Use cases
Struct inheritance
Animal := struct (age: Age)
Cat := struct (this Animal, meow: Sound) // could also write (this animal: Animal, ..)
c := Cat(..)
c.meow : Sound
c.age : Age
alloc_animal := (a: Animal) => {..}
// Both valid
alloc_animal(c)
alloc_animal(c.0)Constraints
Consider an extension of the syntax where P where Q means (this P, infer Q). This can be used to write constraints, with Q being inferred from context if possible.
AsType <bool> ((b) => type b ~ true)
arcsin : (x: f32) where { x in -1..1 } -> (r: f32) where { r in (-PI/2)..(PI/2) }
= (x) => { ... }
bing := (x: f32) => {
if x in -1..1 {
// implicitly exists: _p: {x in -1..1}
print(arcsin(x)) // desugared/inferred to print(arcsin((x, _p)))
}
}Traits
The expression (T: Type) where Foo<T>, where Foo is a struct with a type parameter, can be used to write generics with trait bounds, where Foo represents a trait.
Iterator := struct <I, T> (
next: &mut I -> Option<T>
)
VecIter := struct <T> (
data: &mut Vec<T>,
current_index: usize
)
vec_iter_iterator := <T> => Iterator <VecIter<T>, T> (
next = (v) => {
if v.current_index == len(v.data) {
None
} else {
result := v.data[v.current_index];
v.current_index += 1;
Some(result)
}
}
)
collect_nodes := <I where Iterator<I, Node>> => (nodes: I) -> NodeCollection => {
...
}
build_ast := () => {
node_vec := vec()
// .. build the ast
nodes := collect_nodes(iter(node_vec))
// desugared to:
nodes := collect_nodes<(VecIter<Node>, vec_iter_iterator<Node>)>(iter(node_vec))
pass_to_next_stage(nodes)
}