Update read-only and write-only syntax (#96)

This commit is contained in:
alicesaidhi 2025-01-29 16:27:09 +01:00 committed by GitHub
parent 6c47f8d595
commit 1133124f9c
Signed by: DevComp
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 24 deletions

View file

@ -27,7 +27,7 @@ Add a modifier to table properties indicating that they are read-only.
This proposal is not about syntax, but it will be useful for examples to have some. Write: This proposal is not about syntax, but it will be useful for examples to have some. Write:
* `get p: T` for a read-only property of type `T`. * `read p: T` for a read-only property of type `T`.
For example: For example:
```luau ```luau
@ -37,7 +37,7 @@ end
``` ```
has inferred type: has inferred type:
``` ```
f: (t: { p: number, get q: number }) -> () f: (t: { p: number, read q: number }) -> ()
``` ```
indicating that `p` is used read-write but `q` is used read-only. indicating that `p` is used read-write but `q` is used read-only.
@ -45,27 +45,27 @@ indicating that `p` is used read-write but `q` is used read-only.
Read-only properties are covariant: Read-only properties are covariant:
* If `T` is a subtype of `U` then `{ get p: T }` is a subtype of `{ get p: U }`. * If `T` is a subtype of `U` then `{ read p: T }` is a subtype of `{ read p: U }`.
Read-write properties are a subtype of read-only properties: Read-write properties are a subtype of read-only properties:
* If `T` is a subtype of `U` then `{ p: T }` is a subtype of `{ get p: U }`. * If `T` is a subtype of `U` then `{ p: T }` is a subtype of `{ read p: U }`.
### Indexers ### Indexers
Indexers can be marked read-only just like properties. In Indexers can be marked read-only just like properties. In
particular, this means there are read-only arrays `{get T}`, that are particular, this means there are read-only arrays `{read T}`, that are
covariant, so we have a solution to the "covariant array problem": covariant, so we have a solution to the "covariant array problem":
```luau ```luau
local dogs: {Dog} local dogs: {Dog}
function f(a: {get Animal}) ... end function f(a: {read Animal}) ... end
f(dogs) f(dogs)
``` ```
It is sound to allow this program, since `f` only needs read access to It is sound to allow this program, since `f` only needs read access to
the array, and `{Dog}` is a subtype of `{get Dog}`, which is a subtype the array, and `{Dog}` is a subtype of `{read Dog}`, which is a subtype
of `{get Animal}`. This would not be sound if `f` had write access, of `{read Animal}`. This would not be sound if `f` had write access,
for example `function f(a: {Animal}) a[1] = Cat.new() end`. for example `function f(a: {Animal}) a[1] = Cat.new() end`.
### Functions ### Functions
@ -80,8 +80,8 @@ function t:m() ... end
should have type should have type
``` ```
t : { t : {
get f : () -> (), read f : () -> (),
get m : (self) -> () read m : (self) -> ()
} }
``` ```
@ -107,7 +107,7 @@ since the build method is read-write, so users can update it:
but if we define: but if we define:
```luau ```luau
type ROFactory<A> = { get build : () -> A } type ROFactory<A> = { read build : () -> A }
``` ```
then we do have that `ROFactory<Dog>` is a subtype of `ROFactory<Animal>` then we do have that `ROFactory<Dog>` is a subtype of `ROFactory<Animal>`

View file

@ -46,18 +46,18 @@ The reason for these failures is that neither of these is the most
specific type. It is one which includes that `t.p` is written to, and specific type. It is one which includes that `t.p` is written to, and
not read from. not read from.
```luau ```luau
f : ({ set p: Dog }) -> () f : ({ write p: Dog }) -> ()
``` ```
This allows both example uses of `f` to typecheck. To see that it is more specific than `({ p: Animal }) -> ()`: This allows both example uses of `f` to typecheck. To see that it is more specific than `({ p: Animal }) -> ()`:
* `Dog` is a subtype of `Animal` * `Dog` is a subtype of `Animal`
* so (since write-only properties are contravariant) `{ set p: Dog }` is a supertype of `{ set p: Animal }` * so (since write-only properties are contravariant) `{ write p: Dog }` is a supertype of `{ write p: Animal }`
* and (since read-write properties are a subtype of write-only properties) `{ set p: Animal }` is a supertype of `{ p: Animal }` * and (since read-write properties are a subtype of write-only properties) `{ write p: Animal }` is a supertype of `{ p: Animal }`
* so (by transitivity) `{ set p: Dog }` is a supertype of `{ set p: Animal }` is a supertype of `{ p: Animal }` * so (by transitivity) `{ write p: Dog }` is a supertype of `{ write p: Animal }` is a supertype of `{ p: Animal }`
* so (since function arguments are contravariant `({ set p: Dog }) -> ()` is a subtype of `({ p: Animal }) -> ()` * so (since function arguments are contravariant `({ write p: Dog }) -> ()` is a subtype of `({ p: Animal }) -> ()`
and similarly `({ set p: Dog }) -> ()` is a subtype of `({ p: Dog }) -> ()`. and similarly `({ write p: Dog }) -> ()` is a subtype of `({ p: Dog }) -> ()`.
Local type inference depends on the existence of most specific (and most general) types, Local type inference depends on the existence of most specific (and most general) types,
so if we want to use it "off the shelf" we will need write-only properties. so if we want to use it "off the shelf" we will need write-only properties.
@ -77,7 +77,7 @@ Add a modifier to table properties indicating that they are write-only.
This proposal is not about syntax, but it will be useful for examples to have some. Write: This proposal is not about syntax, but it will be useful for examples to have some. Write:
* `set p: T` for a write-only property of type `T`. * `write p: T` for a write-only property of type `T`.
For example: For example:
```luau ```luau
@ -87,7 +87,7 @@ end
``` ```
has inferred type: has inferred type:
``` ```
f: (t: { set p: number, get q: number }) -> () f: (t: { write p: number, read q: number }) -> ()
``` ```
indicating that `p` is used write-only but `q` is used read-only. indicating that `p` is used write-only but `q` is used read-only.
@ -112,16 +112,16 @@ When declaring a method in a table or class, we should add a read-only property
Write-only properties are contravariant: Write-only properties are contravariant:
* If `T` is a subtype of `U` then `{ set p: U }` is a subtype of `{ set p: T }`. * If `T` is a subtype of `U` then `{ write p: U }` is a subtype of `{ write p: T }`.
Read-write properties are a subtype of write-only properties: Read-write properties are a subtype of write-only properties:
* If `T` is a subtype of `U` then `{ p: U }` is a subtype of `{ set p: T }`. * If `T` is a subtype of `U` then `{ p: U }` is a subtype of `{ write p: T }`.
### Indexers ### Indexers
Indexers can be marked write-only just like properties. In Indexers can be marked write-only just like properties. In
particular, this means there are write-only arrays `{set T}`, that are particular, this means there are write-only arrays `{write T}`, that are
contravariant. These are sometimes useful, for example: contravariant. These are sometimes useful, for example:
```luau ```luau
@ -135,7 +135,7 @@ end
we can give this function the type we can give this function the type
``` ```
move: <a>({a},{set a}) -> () move: <a>({a},{write a}) -> ()
``` ```
and since write-only arrays are contravariant, we can call this with differently-typed and since write-only arrays are contravariant, we can call this with differently-typed
@ -160,7 +160,7 @@ Once we have read-only properties and write-only properties, type intersection
gives read-write properties with different types. gives read-write properties with different types.
```luau ```luau
{ get p: T } & { set p : U } { read p: T } & { write p : U }
``` ```
If we infer such types, we may wish to present them differently, for If we infer such types, we may wish to present them differently, for