Comment by pjmlp

3 days ago

We already have that by having multiple approaches via affine types (what Rust uses), linear types, effects, dependent types, formal proofs.

All have different costs and capabilities across implementation, performance and developer experience.

Then we have what everyone else besides Rust is actually going for, the productivity of automatic resource management (regardless of how), coupled with one of the type systems above, only for performance critical code paths.

> affine types (what Rust uses)

I'd just like to interject for a moment. What you’re referring to as "affine types", is in fact, Uniqueness Types. The difference has to do with how they interact with unrestricted types. In Rust, these "unrestricted types" are references (which can be used multiple times due to implementing Copy).

Uniqueness types allow functions to place a constraint on the caller ("this argument cannot be aliased when you pass it to me"), but places no restriction on the callee. This is useful for Rust, because (among other reasons) if a value is not aliased you can free it and be sure that you're not leaving behind references to freed data.

Affine types are the opposite - they allow the caller to place a restriction on the callee ("I'm passing you this value, but you may use it at most once"), which is not something possible to express in Rust's type system, because the callee is always free to create a reference from its argument and pass that reference to multiple functions..

  • I would say it is perfectly accurate to call Rust's type system affine. At its core, "affine" means that the type system has exchange and weakening but not contraction, and that exactly characterizes Rust's type system. See <https://math.stackexchange.com/questions/3356302/substructur...> for an explanation of what those terms mean (that's in the context of a logic, but it's the same for type systems via the Curry-Howard correspondence).

    This is often explained via the "do not use more than once rule", but that's not the actual definition, and as your example shows, following that simplified explanation to the letter can cause confusion.

    > because the callee is always free to create a reference from its argument and pass that reference to multiple functions..

    Passing a reference is not the same thing as passing the actual value, so this does not contradict affinity.

    • > Passing a reference is not the same thing as passing the actual value, so this does not contradict affinity.

      I agree that passing a reference is not the same thing as passing the actual value. If it were, there would really be no point to references. However, it does contradict affinity. Specifically, the fact that multiple references can be created from the same value, combined with the properties of references, contradicts affinity.

      > At its core, "affine" means that the type system has exchange and weakening but not contraction, and that exactly characterizes Rust's type system.

      Well, the rust type system certainly does support contraction, as I can use a reference multiple times. So what is that if not contraction? It seems like rust at least does support contraction for references.

      But in practice, having absolutely no contraction is not a very useful definition of affine, because no practical programming language would ever satisfy it. It prohibits too much and the language would not even be turing complete. Instead, there is usually an "affine world" and an "exponential world". (Exponential meaning "unrestricted" values that you can do whatever you want with). And the convention is that values can go from the exponential world to the affine world, but not back. So a function taking an affine value can be passed any value, but must use in in an affine way, and meanwhile but a function taking an exponential (unrestricted) value can only be passed exponential and not an affine value.

      If you don't believe me, you can try using linear haskell, and notice that a function taking a linear argument can be passed a non-linear argument, but not the other way around.

      If you interpret Rust's type system this way, it's natural to interpret references as exponentials. But references have the opposite convention. You can go from owned values to references, but not the other way around, which is precisely the opposite situation as the convention around linear/affine type systems. Because these systems feel very different to use and enforce very different properties, I do think it's important that we have separate names for them rather than referring to both as "affine". And the usual name for the rust-like system is "uniqueness types", see https://docs.idris-lang.org/en/latest/reference/uniqueness-t... or https://en.wikipedia.org/wiki/Uniqueness_type .

      9 replies →

  • Yeah, that makes sense. The Rust type system isn't "affine" as in affine logic. Rust allows different forms of contraction, which affine logic strictly prohibits.

    And some people like to claim that the Curry-Howard correspondence proves something about their type system, but this is only true for dependently typed languages.

    And the proofs aren't about program behavior.

    See, https://liamoc.net/forest/loc-000S/index.xml

    • > Rust allows different forms of contraction, which affine logic strictly prohibits.

      That's just wrong. Affine logic totally can have contraction for some propositions.

      Also, CH totally exists for non-dependently-typed languages -- for instance, there is a beautiful correspondence between the simply-typed lambda calculus and propositional logic. Please stop repeating claims that you apparently do not understand.

I would love some sort of affine types in languages like Kotlin, it just makes cleaner code organization in my opinion.

Doesn't matter if it's purely "syntaxical" because the language is garbage collected, just the fact of specifying what owns what and be explicit about multiple references is great imo.

Some sort of effects systems can already be simulated with Kotlin features too.

Programming language theory is so interesting!