logoalt Hacker News

amluto • yesterday at 7:03 PM • 1 reply • view on HN

> The key here is that one _can_ express immutable references in Valen; an immutable reference is a reference that the containing function doesn't express a `mut` effect for. And once we have immutable references, we get all of the nice concurrency benefits that Rust trailblazed.

I'm contemplating this. Is it enough?

Suppose I have an object (I'm not even trying to get the syntax right, especially since Valen's syntax appears a bit different from Rust's):

    let obj: T = ...;
And I also create a structured concurrency thingy in the same scope:

    let workgroup: StructuredConcurrencyThingy = ...;

Now I pass references to both of these down the callchain, through a few functions, maybe via some structs with lifetime parameters, and in the inner function I do this:

    workgroup.submit(move || print_in_rust(obj));
where print_in_rust is a Rust function taking &T. (I haven't the faintest clue how to spell that in Valen.) So I'm making a closure, and the closure captures obj, and the closure needs obj to exist and be immutable for the lifetime of the closure, which exceeds the creating function's lifetime. It's bounded by the workgroup's lifetime, and Rust is fine with this.

But, if I'm understanding you right, the immutability of the referent of obj depends on the signatures of everything in the callchain that might execute during the lifetime of the closure. How does that work?

edit: On further contemplation, I don't think that actual concurrency is needed to illustrate it. I think the same issue exists if I have a T<'a> that has a method that takes an &'a reference (probably like store_a_reference(&mut self, ref: &'a u32)) and dereferences ref both immediately and later and asserts that it sees the same value both times.


Replies

verdagon • yesterday at 8:21 PM

Sorry, I think my attempts to simplify/explain ended up confusing things. I'll try to be a little more precise.

In Rust, a reference is forever shared/immutable or forever unique/mutable.

In Valen, a reference is... "it depends". Specifically, it depends on the context.

It's similar to a &GhostCell<T>, where its mutability isn't determined yet because the GhostToken isn't present yet.

So, what determines the mutability at any given point in time? The function's `mut` effects (or lack of them) for the group/path that the reference is pointing to.

So we can imagine a `execute_on_4_threads` function like this:

    func execute_on_4_threads<C', F: Func<void, (), C>>(
      workgroup: &W,
      closure: F
    ) { ... }
(`Func<void, (), C>` is a trait for a function that returns void, takes no extra parameters, and names its captures as group C).

The most relevant fact here is that this function doesn't declare any `mut` effects at all (not on C, not on W's group, nothing), so nothing is being mutated. (And because of that, this function's callees also can't have any `mut` effects; they also can't mutate the data)

Now let's say we changed `workgroup`'s type to `&W in w`, and added a `mut(w)` to the function, to describe that we might modify the workgroup.

At that point, we would still know that we can invoke the closure from 4 threads, because we declared no relationship between `w` and `C`, so the compiler assumes (and enforces) that they're disjoint, have no overlap, nothing in one aliases anything in the other.

Hopefully that helps. Maybe I should write a blog post on this, my posts are usually clearer than my HN comments.

(Also, I'm not sure I understand your edit, if you could clarify that would be much appreciated)

➕ show 1 reply