Take it easy type constraints

I wanted to make a method that takes any object that has a specific field, and this method does not change the object, but only reads the value of the field. An object can be either an anonymous structure or an instance of a class, so I used a type parameter like {field:String}. Since the method does not change the object, it does not matter if the field is mutable or not. But here the problem arises that the method will not accept objects where the field is final or has access identifiers. I got around this using the abstract. But if the structure should have more than one field, then going through all the options for accessing the field will be a headache. Maybe there is some simpler solution without using macros to explain to the compiler that the field is used only for reading and it doesn’t matter if it is possible to write to it or not?

class Test {
  static function main() {
    justRead({field:1});
    justRead(new FinalField(2));
    justRead(new PropertyField(3));
  }
  
  static function justRead(v:ReadMyField) {
    trace(v.field);
  }
}

class FinalField {
  
  public final field:Int;
  
  public function new(value:Int) {
    this.field = value;
  }
}

class PropertyField {
  
  public var field(default, null):Int;
  
  public function new(value:Int) {
    this.field = value;
  }
}

@:forward
abstract ReadMyField({field:Int}) {
	public function new(value) {
		this = value;
	}

	@:from static inline function finalCast(v:{final field:Int;}) {
		return new ReadMyField(cast v);
	}

	@:from static inline function propertyCast(v:{var field(default, null):Int;}) {
		return new ReadMyField(cast v);
	}
}
2 Likes

Try {final field:Int;} instead of {field:Int}

This constraint will not allow passing an instance of a class with a property (default, null), as well as such a case:

var struct = {field : 1};
justRead(struct);

Test.hx:9: characters 14-20 : error: Cannot unify final and non-final fields
Test.hx:9: characters 14-20 : have: { field: Dynamic }
Test.hx:9: characters 14-20 : want: { field: Dynamic }
Test.hx:9: characters 14-20 : For function argument ‘v’

What about { var field(default,never):Int; }?