Skip to content

Commit b4192ae

Browse files
committed
Add an RFC for ShapeLike, ValueLike.
1 parent 465d188 commit b4192ae

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

text/0035-shapelike-valuelike.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
- Start Date: 2023-12-04
2+
- RFC PR: [amaranth-lang/rfcs#35](https://github.com/amaranth-lang/rfcs/pull/35)
3+
- Amaranth Issue: [amaranth-lang/amaranth#0000](https://github.com/amaranth-lang/amaranth/issues/0000)
4+
5+
# Add `ShapeLike`, `ValueLike`
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
Two special classes are added to the language: `ShapeLike` and `ValueLike`. They cannot be constructed, but can be used to determine with `isinstance` and `issubclass` to determine whether something can be cast to `Shape` or a `Value`, respectively.
11+
12+
## Motivation
13+
[motivation]: #motivation
14+
15+
As it stands, we have multiple types of objects that can be used as shapes (`Shape`, `ShapeCastable`, `int`, `range`, `EnumMeta`) and values (`Value`, `ValueCastable`, `int`, `Enum`). These types have no common superclass, so there's no easy way to check if an object can be used as a shape or a value, save for actually calling `Shape.cast` or `Value.cast`. Introducing `ShapeLike` and `ValueLike` provides an idiomatic way to perform such a check.
16+
17+
Additionally, when type annotations are in use, there is currently no simple type that can be used for an argument that takes an arbitrary shape- or value-castable object. These new classes provide such a simple type.
18+
19+
## Guide-level explanation
20+
[guide-level-explanation]: #guide-level-explanation
21+
22+
In Amaranth, multiple types of objects can be cast to shapes:
23+
24+
- actual `Shape` objects
25+
- `ShapeCastable` objects
26+
- non-negative integers
27+
- `range` objects
28+
- `Enum` subclasses with const-castable values
29+
30+
To check whether an object is of a type that can be cast to a shape, `isinstance(obj, ShapeLike)` can be used. To check whether a type can be, in general, cast to a shape, `issubclass(cls, ShapeLike)` can be used.
31+
32+
Likewise, multiple types of objects can be cast to values:
33+
34+
- actual `Value` objects
35+
- `ValueCastable` objects
36+
- integers
37+
- values of `Enum` subclasses with const-castable values
38+
39+
To check whether an object is of a type that can be cast to a value, `isinstance(obj, ValueLike)` can be used. To check whether a type can be, in general, cast to a value, `issubclass(cls, ValueLike)` can be used.
40+
41+
## Reference-level explanation
42+
[reference-level-explanation]: #reference-level-explanation
43+
44+
A `ShapeLike` class is provided. It cannot be constructed, and can only be used with `isinstance` and `issubclass`, which are overriden by a custom metaclass.
45+
46+
`issubclass(cls, ShapeLike)` returns `True` for:
47+
48+
- `Shape`
49+
- `ShapeCastable` and its subclasses
50+
- `int` and its subclasses
51+
- `range` and its subclasses
52+
- `enum.EnumMeta` and its subclasses
53+
54+
`isinstance(obj, ShapeLike)` returns `True` for:
55+
56+
- instances of `Shape`
57+
- instances of `ShapeCastable` and its subclasses
58+
- non-negative `int` values (and `int` subclasses)
59+
- `enum.Enum` subclasses where every value is a `ValueLike`
60+
61+
Similarly, a `ValueLike` class is provided.
62+
63+
`issubclass(cls, ValueLike)` returns `True` for:
64+
65+
- `Value` and its subclasses
66+
- `ValueCastable` and its subclasses
67+
- `int` and its subclasses
68+
- `enum.Enum` subclasses where every value is a `ValueLike`
69+
70+
`isinstance(obj, ValueLike)` returns `True` iff `issubclass(type(obj), ValueLike)` returns `True`.
71+
72+
## Drawbacks
73+
[drawbacks]: #drawbacks
74+
75+
More moving parts in the language.
76+
77+
`isinstance(obj, ShapeLike)` does not actually guarantee that `Shape.cast(obj)` will succeed — the instance check looks only at surface-level information, and an exception can still be thrown. `issubclass(cls, ShapeLike)` is, by necessity, even more inaccurate.
78+
79+
## Rationale and alternatives
80+
[rationale-and-alternatives]: #rationale-and-alternatives
81+
82+
There are many ways to implement the instance and subclass checks, some more precise (and complex) than others. The semantics described above are a compromise.
83+
84+
For `isinstance`, a simple variant would be to just try `Shape.cast` or `Value.cast` and see if it raises an exception. However, this will sometimes result in `isinstance(MyShapeCastable(), ShapeLike)` returning `False`, which may be very unintuitive and hide bugs.
85+
86+
The check for a valid shape-castable enum described above is an approximation — the actual logic used requires all values of an enum to be *const*-castable, not just value-castable. However, there is no way to check this without actually invoking `Value.cast` on the enum members.
87+
88+
## Prior art
89+
[prior-art]: #prior-art
90+
91+
Python has the concept of abstract base classes, such as `collections.abc.Sequence`, which can be used for subclass checking even if they are not actual superclasses of the types involved. `ShapeLike` and `ValueLike` are effectively ABCs, though they do not use the actual ABC machinery (due to having custom logic in instance checking).
92+
93+
## Unresolved questions
94+
[unresolved-questions]: #unresolved-questions
95+
96+
- Should the exact details of the instance and subclass checks be changed?
97+
98+
## Future possibilities
99+
[future-possibilities]: #future-possibilities
100+
101+
A similar ABC-like class has been proposed for `lib.wiring` interfaces.

0 commit comments

Comments
 (0)