Skip to content

Commit 80e9f22

Browse files
committed
Add RFC to allow overriding Value operators.
1 parent 4edf8cc commit 80e9f22

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
- Start Date: 2023-10-30
2+
- RFC PR: [amaranth-lang/rfcs#0028](https://github.com/amaranth-lang/rfcs/pull/0028)
3+
- Amaranth Issue: [amaranth-lang/amaranth#0929](https://github.com/amaranth-lang/amaranth/issues/0929)
4+
5+
# Allow overriding Value operators
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
Allow overriding binary `Value` operators with reflected operators in a value-castable type.
11+
12+
## Motivation
13+
[motivation]: #motivation
14+
15+
A value-castable type can define operators that return another value-castable.
16+
However, if the left side operand is a `Value`, its operator will be called first, casting the right side operand to a plain `Value`.
17+
This creates a mismatch in behavior depending on the type and order of operands.
18+
19+
As an example, consider the multiplication of a fixed point value-castable with an integral type:
20+
```
21+
>>> Q(7).const(0.5) * 255
22+
(fixedpoint Q8.7 (* (const 8'sd64) (const 8'd255)))
23+
>>> 255 * Q(7).const(0.5)
24+
(fixedpoint Q8.7 (* (const 8'sd64) (const 8'd255)))
25+
>>> Q(7).const(0.5) * C(255)
26+
(fixedpoint Q8.7 (* (const 8'sd64) (const 8'd255)))
27+
>>> C(255) * Q(7).const(0.5)
28+
(* (const 8'd255) (const 8'sd64))
29+
```
30+
31+
## Explanation
32+
[guide-level-explanation]: #guide-level-explanation
33+
34+
When a binary `Value` operator is called with a value-castable `other`, check whether the value-castable implements the reflected variant of the operator first and defer to it when present.
35+
36+
## Drawbacks
37+
[drawbacks]: #drawbacks
38+
39+
Extra logic required around every `Value` operator.
40+
41+
## Prior art
42+
[prior-art]: #prior-art
43+
44+
This is standard behavior for inheritance in [Python](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types):
45+
46+
> Note: If the right operand’s type is a subclass of the left operand’s type and that subclass provides a different implementation of the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations.
47+
48+
We don't get this behavior automatically because `Value` is not an ancestor of `ValueCastable`, but it would make sense for it to behave as it were.
49+
50+
## Rationale and alternatives
51+
[rationale-and-alternatives]: #rationale-and-alternatives
52+
53+
As an alternative, `Value` and `ValueCastable` could be rearchitected so that `ValueCastable` inherits from either `Value` or a common base that implements the `Value` operators.
54+
This would make Python do the right thing w.r.t. operator overriding, but is a larger change with more potential for undesirable consequences.
55+
56+
57+
## Unresolved questions
58+
[unresolved-questions]: #unresolved-questions
59+
60+
None.
61+
62+
## Future possibilities
63+
[future-possibilities]: #future-possibilities
64+
65+
`Value.eq()` could in the same manner check for and defer to a `.req()` method, i.e. reflected `.eq()`, to allow a value-castable to override how assignment from it to a `Value` is handled.

0 commit comments

Comments
 (0)