HaxeFoundation / HaxeFoundation/haxe
final vs never vs null
- Dominant language
- Haxe
- Stars
- 6.9k
- Forks
- 715
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 11
Description
Following up on #7818:
```haxe
class Main {
static function main() {
var aFinal:{ final foo:Int; } = { foo: 42 };// works, because of top down inference
var aNever:{ var foo(default, never):Int; } = aFinal;// no problem
var aNull:{ var foo(default, null):Int; } = aNever;// still no problem
aNull = aFinal;// Cannot unify final and non-final fields ... what?
}
}
```
This is weird. One could argue that unifying `never` with `null` creates a type hole: once access is `null`, you can use `@:privateAccess` to read/write the value. You can even start with `final`, promote to `never`, promote to `null` and mutate via `@:privateAccess`. One "solution" would be to disallow `never` to unify with `null` but it'd break a lot of existing code and make things more awkward than they already are.
I would propose that:
1. `null` and `never` get treated as being the same for structures/interfaces and `final` may unify with both. Code written against read-only structures/interfaces should be able to operate on immutable objects (just not the other way around).
2. `@:privateAccess` cannot access `null` on structures/interfaces, only on classes. Reasons:
- it closes the "type hole"
- I'm relatively optimistic that very little code actually uses `@:privateAccess` on structures/interfaces
- it actually makes sense to only allow bypassing access restrictions on concrete implementations, because on structures/interfaces it can go absolutely wrong:
```haxe
interface HasFoo { var foo(get, null):Int; }
class Test implements HasFoo {
@:isVar public var foo(get, set):Int;
function get_foo() return foo;
function set_foo(param:Int)
return
this.foo = if (param > 100) 100;
else if (param < 0) 0;
else param;
function new() {}
static function main() {
var t = new Test();
t.foo = 1000;
trace(t.foo);//100 - of course ... foo will never be > 100
var f:HasFoo = t;
@:privateAccess f.foo = 1000;
trace(t.foo);//1000 ... whahooops!
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.