calebh / Juniper

https://www.juniper-lang.org/
MIT License
79 stars 9 forks source link

inout & mut type decoration location #23

Closed Qata closed 1 week ago

Qata commented 2 weeks ago

Hello again!

Not exactly a critical or massive issue, but wanted to share the Swift evolution document about how and why they moved the inout argument decorator from affecting the parameter name to the type itself.

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0031-adjusting-inout-declarations.md

calebh commented 2 weeks ago

inout is actually already part of the type system. It's a bit difficult to see in most Juniper programs, however it is possible to store generic functions with inout parameters inside of types:

type con<closure, a, b> = container((closure)(a) -> b)

// mypred has type (inout int32) -> bool
fun mypred(inout n : int32) = {
    n += 42
    n <= 50
}

let foo : con<_, inout int32, bool> = container(mypred)

It is also possible to annotate a function argument as inout on the type side, and pass it to another inout function without an inout annotation at the callsite. For example:

fun g(inout b : bool) = b

fun f(x : inout bool) = {
    // We can't mutate x here, however we can pass it as an inout parameter to another inout function
    g(x) // Note that we pass x without inout
}

Regarding the use of inout on the caller side: I want to make it clear when you pass a value to a function that it might mutate an argument you pass in. This is only possible with a the special inout keyword. There are other benefits: we can enforce at the parsing stage that an inout argument expression must be a LHS (left hand side) expression. The only way to "create" an inout value is by using the keyword at the call site.

Similarly, the only way to consume an inout value is by "pattern matching" against it in a function head. I think that having inout as a special keyword in the function head helps type inference a bit. A function argument has a unique inferred type, instead of having two options: "a" and "inout a".

There is a lot of checks to ensure that you cannot accidentally hide an "inout" parameter inside a data structure. This ensures that references aren't saved, and can really only show up inside of function heads.

Qata commented 1 week ago

Thank you, this is all good information. I'm planning to eventually expand the features document and I'll add insights from here when I do.