Skip to content

More accurate constructor argument typing #98

Open
@ducky64

Description

@ducky64

Currently, constructors (__init__) take *Like types (eg, StringLike = Union[str, StringExpr]), which was needed to allow literal value shorthand in constructor calls. However, the @init_in_parent provides a translation later that actually narrows the types, __init__ still accepts the *Like types on a call, but internally (seen by __init__) is all *Expr types.

Prior to #96, all constructor parameters should be re-mapped via self.param = self.Parameter(...) and given an explicit type. #96 changes the constructors to infer parameter types based on typing annotations, so for example if we had param as part of the constructor argument list, we could directly use self.param = param. The downside is that param is of type *Like (from the constructor) instead of the more narrow and accurate *Expr (returned by self.Parameter).

This causes a mild issue with generators, since they should only take *Expr types. The current workaround is to expand generators to also take the castable-types in addition to the *Expr types - which isn't completely accurate, but continues to provides convenient lightweight syntax in exchange for a bit of leakiness in the type checking of an advanced feature.

Properly typing init_in_parent

Ideally, one solution would be to define the init_in_parent decorator type, such that the __init__ can be properly defined in terms of *Expr type, and the decorator would provide a wrapped function that takes the *Like type. The problem is that functions (Callables) have contravariant types for the argument list, so it can't capture the ConstraintExpr[...] type parameters (for example, it doesn't accept BoolExpr in place of the ConstraintExpr[...] pattern).

Additionally, having an *Expr in the type signature of __init__, even if the decorator is properly typed, may be confusing, since users might see the __init__ type signature and wonder why a literal type is allowed. However, the @init_in_parent decorator could signpost that, and since the behavior is consistent it's only one additional learning curve item.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions