Hey, sorry for the late reply, no idea if you'll check this still on hackernews (though I do have you on discord so I can poke you there if need be ;) ).
I was experimenting with implementing something Hylo-like (formerly Val). Meaning no references what so ever, only parameter passing modes. But I hated their subscript mechanism (a form of coroutine, used to implement iterators and views, similar to what Graydon originally wanted for Rust).
I wanted to have what they call subscripts work as regular functions, so I needed some way for a function to return a reference-like thing. This gets you down the rabbit hole of extending second-class references until they're first-class.
Hylo already tracks paths implicitly in the function arguments, it needs to do so to maintain value semantics (if you have two references mutating the same thing, value semantics break). I wasn't particularly worried about the value semantics, what I wanted were nice to work with references. The first thing was how do you return a reference from a function in a way that is safe?
Well, if you apply the same rule as subscripts (that they can only appear as arguments to other functions, not assigned to variables), then everything just works out. The only thing I needed to know was what did the reference point to? Well, Hylo tracks paths, but I need to say which path, so I had something like the following:
// returns the longest slice of v1 that shares a prefix with v2
fun matching(in v1: Vec<int>, in v2: Vec<int>) -> &Slice<int> of v1
That `of v1` annotation essentially said "this reference has the same path as v1". I didn't give you a way to be more specific about the paths like you allow (I love that idea!), you could only be specific about
which argument it was a reference of, and the path was inferred from that.
There were more complicated cases I could think of that required the thing after `of` to be a set:
// returns the longest of s1 or s2
// output must be conservative
fun longest(in s1: &Slice<int>, in s2: &Slice<int>) -> &Slice<int> of {v1, v2}
In this case the slice can be of either input and the compiler has to be conservative about it.
Once this was in place then it was a matter of expanding things where you could store Slices (and single element references) in variables, as long as they were in the same scope as their path, and where you could store Slices and Refs inside structs (which then become a view type as well, like C#'s ref structs).
I ended up getting frustrated with it and tapping out, but I'm glad there are people exploring this path route. It is much better IMO than abusing parametric polymorphism to track lifetimes.
I was experimenting with implementing something Hylo-like (formerly Val). Meaning no references what so ever, only parameter passing modes. But I hated their subscript mechanism (a form of coroutine, used to implement iterators and views, similar to what Graydon originally wanted for Rust).
I wanted to have what they call subscripts work as regular functions, so I needed some way for a function to return a reference-like thing. This gets you down the rabbit hole of extending second-class references until they're first-class.
Hylo already tracks paths implicitly in the function arguments, it needs to do so to maintain value semantics (if you have two references mutating the same thing, value semantics break). I wasn't particularly worried about the value semantics, what I wanted were nice to work with references. The first thing was how do you return a reference from a function in a way that is safe?
Well, if you apply the same rule as subscripts (that they can only appear as arguments to other functions, not assigned to variables), then everything just works out. The only thing I needed to know was what did the reference point to? Well, Hylo tracks paths, but I need to say which path, so I had something like the following:
That `of v1` annotation essentially said "this reference has the same path as v1". I didn't give you a way to be more specific about the paths like you allow (I love that idea!), you could only be specific about which argument it was a reference of, and the path was inferred from that.There were more complicated cases I could think of that required the thing after `of` to be a set:
In this case the slice can be of either input and the compiler has to be conservative about it.Once this was in place then it was a matter of expanding things where you could store Slices (and single element references) in variables, as long as they were in the same scope as their path, and where you could store Slices and Refs inside structs (which then become a view type as well, like C#'s ref structs).
I ended up getting frustrated with it and tapping out, but I'm glad there are people exploring this path route. It is much better IMO than abusing parametric polymorphism to track lifetimes.