Variable Bindings proposal discussion

ref is a parametric mutable reference type, which means it inherits the mutability of the type it references, for example:


fn get_first_element(ref my_list: List[Int]) -> ref [my_list] Int:
    ref first = my_list[0] # first is parametric mutable reference which inherits mutability from my_list
    return first

fn f(read immutable_list: List[Int], mut mutable_list: List[Int]):
    ref x = get_first_element(mutable_list) # x is mut
    x = 10 # this modifies the first element of mutable_list

    ref y = get_first_element(immutable_list) # y is read
    y = 20 # error: cannot assign to immutable reference

    # the same thing happens with for loops
    for ref element in mutable_list:
            element = 30 # this modifies the current element of mutable_list
    for ref element in immutable_list:
            element = 40 # error: cannot assign to immutable element

Thank you for the clarification.

1 Like

This idea is interesting. I’ll even toss in my wish for mut and read to be able to be explicitly set e.g.

var items = [1, 2, 3]
mut first = items[0]
first += 1
read second = items[1]
# error: cannot assign to an immutable reference, create a local
# copy with `var` or mutate the element in-place using `mut`
second += 1

Besides, having read be the default assignment instead of var I think will eliminate most accidental copies. While it also avoids accidental mutations and makes the programmer use var only when necessary.

The only place where making read the default might get kind of annoying is:

items = [1, 2, 3] # implicitly `read`
# error: cannot create a mutable reference to an immutable reference,
# create a local copy with `var` or mutate the element in-place
# declaring the immutable reference as `mut` or using `var`
mut first = items[0]

which would basically force code where many variables are declared without using var to use it explicitly when wanting to mutate them. And this would make Mojo feel more like Rust than Python; whether that’s good or bad is up to our consensus :man_shrugging:

I do like this from @christoph_schlumpf since it’s a nice library based solution.

2 Likes

I started a topic for this: The case for explicit read variable bindings

4 Likes

… and loosely related: Private struct members - #10 by christoph_schlumpf

1 Like

Is this totally right? ref is parametric but not necessarily itself mutable, but can be mutable or immutable depending on what it infers no? Your example is helpful though in understanding this.

I’ve been playing with mojo for a few months now, and ref is still super intimidating. Everytime I see ref used, I subconsciously read it as why are they specifying ref on an entity that is already a reference?.

Bear with me as I try to walk through this, see if I understand correctly:

The reason element is mutable here is not because ref is mutable, but because ref infers the mutability of some_list which is mutable.

for ref element in some_list:
    print(element) # mutable

Its just a little confusing to understand because by default in for loops element is immutable, so why doesn’t ref just infer the element mutability and keep the immutability property. I understand that ref infers the mutability of some_list, but it doesn’t read (pun[1]) that way to me. Additionally, reading the docs, ref also acts as either indicating a return value should be a reference with some im/mutability, or replaces a preexisting reference specifier like read / mut / owned.

Am I understanding this correctly?

I’m in favor of including read/mut as part of the bindings, at least so beginners are thrown for less of a loop (pun[2]?). Tossing mut before element involves less context / brain cells I guess.

Maybe my issue is that ref and its relationship to how read / mut / owned are used feels inconsistent. Like, ref in some parts of mojo is simply a generic / customizable version of read / mut / owned, but then other places like returns, loops, with, auto-deref, it’s its own concept / entity.

I think the goal of ref is its a parametric version of read / mut / (owned?)? But right now we cant use read / mut beyond function args, so ref feels like it does/is something completely different.

1 Like

There is one thing I don’t understand.
Var = Owned
Ref = Ref
Read = Read
Mut = Mut

This kinda feels like type annotations in java or c land, less typing but more mental overhead.

Maybe owned will be renamed to var at some point.

Discord : "The other ā€œobviousā€ thing falling out of all these improvements is that we should rename ā€˜owned’ to ā€˜var’.

1 Like