Implicit type conversion (like bool <-> int, Bytes32 -> Root, etc) are often considered to be a not really good idea.
However, there are cases with the current beacon chain specs, which demand it.
- Modifying stable specs (like phase0) may be not desirable, e.g. see this discussion
- Implicit conversion of a Python list to SSZ
List/Vector can be clumsy and hinder readability
For example, compare
exposed_derived_secrets=[()] * EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
vs
exposed_derived_secrets=Vector[List[ValidatorIndex, MAX_EARLY_DERIVED_SECRET_REVEALS * SLOTS_PER_EPOCH],
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS](
[List[ValidatorIndex, MAX_EARLY_DERIVED_SECRET_REVEALS * SLOTS_PER_EPOCH]()] *
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
)
Often, a method or a constructor performs implicit conversion, like SSZ constructors or field setters. However, it's not clear how to express the conversion explicitly with type system.
For example, BeaconState has about 29 fields and many of them support implicit conversions, in the sense that if one constructs a BeconState or sets its field with a value of a wrong but compatible type, then the implicit conversion is performed. E.g. python list can be converted to SSZ List or Vector, int can be converted to uint64, bool can be converted to boolean, etc.
That can be in theory modeled with union types, e.g. fieldA: Union[PyList[ElemType],List[ElemType,...]], however, accessing such field will become a problem. Basically, one need to specify a different type signature for getter and setters (and perhaps, for constructors/copy methods of an enclosing object).
So, this looks like a rather ugly solution.
A simpler approach is to specify a list of admissible implicit conversions. For example, there can be rules like:
- if there is an expression
expr of PyList[U] type, but its expected type is List[T,N]. Convert the expr using List[T,N](expr), provided that U is a subtype of T or can be converted to T using implicit conversion rules.
- python
bool can be converted to SSZ boolean and vice versa, using boolean(expr) or bool(expr)
- exprs of
BytesN types can be implicitly converted to corresponding BytesN subclasses (e.g. Root, BLSPubkey, etc)
Note however, the last kind of implicit conversion can still be dangerous, i.e. hide a potential bug. E.g. Root and Domain are both subclasses of Bytes32, so Domain is a subclass of Bytes32 and given the implicit conversion to Root, one can use Domain object where Root is expected. One thus may need to restrict such conversions to cases, where the lower bound is known, e.g. Bytes32 constant can be implicitly converted to Root or Domain, but not a variable of Bytes32 type,
Implicit type conversion (like
bool <-> int,Bytes32 -> Root, etc) are often considered to be a not really good idea.However, there are cases with the current beacon chain specs, which demand it.
List/Vectorcan be clumsy and hinder readabilityFor example, compare
vs
Often, a method or a constructor performs implicit conversion, like SSZ constructors or field setters. However, it's not clear how to express the conversion explicitly with type system.
For example,
BeaconStatehas about 29 fields and many of them support implicit conversions, in the sense that if one constructs aBeconStateor sets its field with a value of a wrong but compatible type, then the implicit conversion is performed. E.g. python list can be converted to SSZListorVector,intcan be converted touint64,boolcan be converted toboolean, etc.That can be in theory modeled with union types, e.g.
fieldA: Union[PyList[ElemType],List[ElemType,...]], however, accessing such field will become a problem. Basically, one need to specify a different type signature for getter and setters (and perhaps, for constructors/copy methods of an enclosing object).So, this looks like a rather ugly solution.
A simpler approach is to specify a list of admissible implicit conversions. For example, there can be rules like:
exprofPyList[U]type, but its expected type isList[T,N]. Convert theexprusingList[T,N](expr), provided thatUis a subtype ofTor can be converted toTusing implicit conversion rules.boolcan be converted to SSZbooleanand vice versa, usingboolean(expr)orbool(expr)BytesNtypes can be implicitly converted to correspondingBytesNsubclasses (e.g.Root,BLSPubkey, etc)Note however, the last kind of implicit conversion can still be dangerous, i.e. hide a potential bug. E.g.
RootandDomainare both subclasses ofBytes32, soDomainis a subclass ofBytes32and given the implicit conversion toRoot, one can useDomainobject whereRootis expected. One thus may need to restrict such conversions to cases, where the lower bound is known, e.g.Bytes32constant can be implicitly converted toRootorDomain, but not a variable ofBytes32type,