question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Affine types / ownership system

See original GitHub issue

It would be amazing if typescript could gain types with an ownership semantics as it could provide a very powerful tool against bugs caused by mutations. Idea is inspired by Rust’s owneship system and adapted to match ts / js language. Idea is to provide built-in types for which move semantics would be tracked. In the example below I’ll refer to that type as Own<t>

Ensures that there is exactly one binding to any given Own<t>, if it is assigned to another binding type of the former binding should become Moved<t> and type of the new binding should be Own<t>.

const array:Own<number[]> = [1, 2, 3]
const array2 = array // => <Own<number[]>> [1, 2, 3]
const first = array[0] <- [TS]: `Moved<number[]>` has no index signature.

A similar thing should happens if function is passed Own<t>, binding in the caller scope will get type Moved<t>:

function take(array: Own<number[]>) {
    // What happens here isn’t important.
}

let array:Own<number[]> = [1, 2, 3];

take(array);

array[0] // <- [TS]: `Moved<number[]>` has no index signature.

Rust also has also borrowing semantics which may be also worse pursuing. But for now I’m going to leave it out until I get some initial feedback.

Why

Typescript already provides tools to deal with the same class of issues using readonly feature, which is nice but mostly useful with immutable data structures that do impose an overhead. With an ownership system it would be possible to author performance critical code without an overhead while still maintaining safety guarantees from the type system.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:22
  • Comments:14 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
ghostcommented, Feb 25, 2019

Affine/linear types would be extremely cool to have for state machines (e.g. chainable/fluent APIs where prior states cannot be reused), not just memory management/immutability/etc.

Productivity shouldn’t be an issue if it was opt-in (like in @Gozala’s example).

2reactions
emdashcommented, Sep 29, 2019

Yeah, I think this would be an amazing thing to see in TypeScript. It would even nudge me to favor TS over Flow for certain applications, because it would be better at eliminating a certain class of bugs.

If it helps, think of it as “Resource Management”, or “Resource Types” rather than the inscrutable “substructural.” Because the primary use is to model a fixed set of resources that can be shared among any number of owners. But in concrete terms, what they do is let you catch the following kinds of errors at compile time, rather than run-time (and hence eliminating the run-time overhead of a library implementation).

  • “Tried to use some single-use x after an operation consumed it”
  • “Forgot to properly release x”
  • “Forgot to actually use x, when failing to do so would be Very Bad”.
  • “Tried to make an unauthorized copy or alias of x, when doing so would be Very Bad”.

Because TypeScript objects don’t have destructors, and most types have reference semantics, it’s also hard to imagine pulling this off as a library. You’d have to implement it as a plugin, or additional tooling.

And any library solution would impose run-time overhead, and be limited to catching bugs at run-time. But as a language feature, something like Unique<T> could theoretically be type-erased back to T prior to code generation. And could even allow for additional optimizations down the road.

It doesn’t necessarily have to be handled as types “changing” as I saw in some earlier comments. You could potentially provide a set of primitives which enable certain static guarantees.

  • singleton - statically enforce (there are a few approaches I can think of) that only a single instance can ever exist. There are a few different approaches with different trade-offs. The easiest to explain is that a “singleton” is just a class where all members are implicitly static, and the constructor runs exactly once. All references to some singleton S refer to the same object.
  • move_only - statically enforce that only one binding to some T can exist at a time. I.e. if you pass a move_only` to a function, you can’t use that binding again until you’ve assigned to it. But callees are allowed to return the T back to you, or pass it on.
  • single_use - statically enforce that some T is used at most once. There needs to some scheme to determine when a value is considered “consumed”.
  • must_use - Make sure the value gets consumed somewhere.

Note that some combinations of these types make sense: single_use + must_use compose to give you “use exactly once”. A type could theoretically be single_use without being move_only. It makes perfect sense to have a move_only singleton, or even a move_only must_use singleton.

Closures do complicate things, but I suspect there conservative solutions that won’t break the language while still providing some utility.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What are affine types in Rust? - help
Affine types is another word for move semantics. All Rust types that aren't Copy (like raw integers and references) are affine. 2 Likes....
Read more >
Practical Affine Types - Northwestern Computer Science
Variables of affine or polymorphic kind cannot be contracted. Our kind system is enriched with dependent kinds.
Read more >
Affine types, which Rust more or less implements with its ownership ...
Affine types, which Rust more or less implements with its ownership system are very close to linear types, see https://en.m.wikipedia.org/wiki/ ...
Read more >
Rust and affine types - did I get it right? : r/rust - Reddit
Affine types in and of themselves are not limited to this pattern though. Affine types lend themselves well to the entire ownership model,...
Read more >
The Ownership Monad - Oregon State University
The language-level ownership typing in Rust has been directly inspired by affine type systems. 2.2 Uniqueness Types.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found