# `AdditiveArithmetic` conformance synthesis for structs

We've been experimenting with synthesizing conformances to `AdditiveArithmetic` (and other protocols) as part of differentiable programming in Swift, and we'd like to start pitching these through Swift Evolution.

`AdditiveArithmetic` generalizes types that define addition, subtraction, and a zero. Conforming types include both numeric scalar types, like `Int` and `Float`, as well as vector types like `SIMD4<Double>` (this is not implemented yet because of ABI concerns).

Similar to `Equatable` and `Hashable` synthesis, `AdditiveArithmetic` conformance synthesis for structs works when all stored properties conform to `AdditiveArithmetic`.

``````struct Point<T: AdditiveArithmetic>: @memberwise AdditiveArithmetic {
var x, y: T

// Compiler synthesizes:
static var zero: Point {
return Point(x: T.zero, y: T.zero)
}

static func + (lhs: Point, rhs: Point) -> Point {
return Point(x: lhs.x + rhs.x, y: rhs.y + rhs.y)
}

static func - (lhs: Point, rhs: Point) -> Point {
return Point(x: lhs.x - rhs.x, y: rhs.y - rhs.y)
}

static func += (lhs: inout Point, rhs: Point) {
lhs.x += rhs.x
lhs.y += rhs.y
}

static func -= (lhs: inout Point, rhs: Point) {
lhs.x -= rhs.x
lhs.y -= rhs.y
}
}

var point = Point<Float>(x: 2, y: 3)
print(point + point) // Point<Float>(x: 4.0, y: 6.0)
print(Point<Float>.zero) // Point<Float>(x: 0.0, y: 0.0)
``````

We believe `AdditiveArithmetic` conformance synthesis is important for numerical computing: it makes aggregates of numeric scalars or vectors just work with addition. Currently, itâ€™s an important usability feature for differentiable programming in Swift.

Edit: As some have pointed out, memberwise derivation of `AdditiveArithmetic` doesn't make sense as a default implementation in all cases, unlike `Equatable` and `Hashable` synthesis. Thus, derivation is gated by the `@memberwise` attribute.

Feedback is welcome!

5 Likes

Unlike `Equatable` and `Hashable`, I suspect the synthesized conformance to `AdditiveArithmetic` would end up wrong fairly often.

``````struct Fraction : AdditiveArithmetic {
var numerator: Int
var denominator: Int
}
var bearing: Double
var distance: Double
}
``````
12 Likes

I'm with @SDGGiesbrecht on this one. You named your example Point, but a Point shouldn't actually conform to AdditiveArithmetic anyway (it would be a Vector). The elaboration of that is that there are an awful lot of types where subtraction gives you a different type.

EDIT: More generally, default implementations should always be sensible and unsurprising, and I'm not sure that "memberwise addition" fits both of those adjectives.

3 Likes

This is essentially a user choice. If they declare a type to conform to `AdditiveArithmetic` with no custom implementations, they are declaring that this type is an additive group and thus requirements implementations are derived field-wise.

1 Like

I have to agree with @SDGGiesbrecht and @jrose here. Synthesized conformances especially (the synthesized default is not easily inspectable by the end user) but any default implementation generally should be predictable and appropriate for the overwhelming majority (if not all) cases.

(I suspect some degree of stretching this will be inevitable when ABI stability restrictions force any new additions to an existing protocol to have a default implementation, but thatâ€™s not at play here.)

A default implementation shouldnâ€™t be simply one among many possibilities for what might be semantically correct. Otherwise, correctly conforming to a protocol becomes pervasively a two-step opt-in + opt-out process.

The calculus becomes different if, say, the protocol in question were a hypothetical `FieldwiseAdditiveArithmetic`. In other words, I think there would be broad agreement as to synthesized conformance if the semantics implied by protocol conformance were sufficient to guarantee the correctness of the synthesized default.

Or, put another way, a default implementation should be correct relying only on the semantics guaranteed by the protocol conformance itself, not on additional semantics to be implied by the absence of an overriding concrete implementation of the requirement.

2 Likes

``````struct Foo: @deriving AdditiveArithmetic {
var x, y: Int
}
``````

The existing `Equatable` and `Hashable` derived conformances can also move to this model.

A keyword for derived conformances has already considered and explicitly rejected in favor of the current design. This allows us to think of synthesized conformances just like magical default implementations that with time could eventually be written in native Swift.

The problem here with whatâ€™s pitched has nothing to do with the syntax of synthesized conformances. The point is that `AdditiveArithmetic` as a protocol does not have semantics such that a default implementation of its requirements (magical or not) would be correct for conforming types. The objection is that conformance to a protocol + non-overriding of a default implementation cannot be used to imply additional semantics.

I agree with your analysis. However, the problem is making product space structs conform to a protocol using field-wise implementations. Requiring the user to manually implement every requirement is not practical, and type class derivation is a standard feature in many languages. I'm very interested in hearing what you think in terms of solving the actual problem to improve language expressivity.

Swift has this feature, both in the form of default implementations via protocol extensions, and in a few cases with synthesis on the compiler. The part that's missing is "field-wise"; when a field-wise implementation is not the {obvious, primary, least surprising} way to implement a protocol, it shouldn't be the default.

Xiaodi's suggestion of a dummy "FieldwiseAdditiveArithmetic" protocol that refines AdditiveArithmetic is one way around this problem. I don't love it because it's a one-off answer to what's probably a general concern, but it is a valid suggestion, and it's in line with how default implementations in the language work (`extension Hashable where Self: RawRepresentable`, for example).

1 Like

Ultimately we would be able to write custom Conformance Implementor in native Swift, but we'll likely need variadic generic first.

1 Like

Hmm, would adding a `@fieldwise` attribute solve the general concern? What's the concern with adding this attribute to specifically solve the non-{obvious, primary, least surprising} field-wise conformance derivations (which, say, will not include `Hashable` and `Equatable` because they are obvious, primary and least surprising)?

It solves that concern for me, yeah. Whether or not people feel like it generally fits with the direction of the language is different, but personally it seems like a case of "explicit opt-in" for something that (currently?) can only be done by the compiler.

Got it, thanks! Unless there is major pushback in this thread, @dan-zheng let's switch to pitching a `@fieldwise` attribute along with `AdditiveArithmetic` derivation instead.

Without commenting on this direction in general, I think `@memberwise` or `@propertywise` would be better colors for this bikeshed. They are more aligned with Swiftâ€™s existing terminology.

4 Likes

I agree about using the existing terminology. And `@storedPropertywise` would be more accurate and less surprising.

`@memberwise` is pretty good too, as it is already being used in the official language reference to refer to stored properties.

## Memberwise Initializers for Structure Types

Structure types automatically receive a memberwise initializer if they donâ€™t define any of their own custom initializers. Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that donâ€™t have default values.

The memberwise initializer is a shorthand way to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name.

3 Likes

`@memberwise` seems fine and may even potentially find applications elsewhere; I think `@memberwise AdditiveArithmetic` would be sufficiently clear.

7 Likes

I would want to see some other use cases before committing to `@memberwise` or similar. As mentioned, the most straightforward solution here, requiring no new attributes, would be a `MemberwiseAdditiveArithmetic` protocol that opts in to the synthesis.

Unless it's possible for `@memberwise` to remove the need for any special support for `AdditiveArithmetic` in the compiler? Would it be possible for `@memberwise` to work for a range of different protocols, with the compiler synthesising the â€śobviousâ€ť memberwise implementation if all the members conformed to a protocol? And what restrictions would that imply on the protocol?

1 Like

This works for `AdditiveArithmetic` because that type is essentially a group, and any direct product of groups is itself a group.

It won't work for any algebraic structure because e.g. the product of two fields is not itself a field (I think there is currently no abstraction in the Swift stdlib for fields, but it can of course be added manually). Of course, you can extend all the operations to the members in a straightforward way, but the thing will work differently than you expect it to. So I'm not sure it's really what you would want (basically, one of the problems is that the compiler can't verify certain laws that you would like to hold).

1 Like

Right. Any direct product of groups can be made into a group in the "obvious" way, but as several others have noted, the result is not necessarily the group that you actually want. Some examples:

• fractions.
• duration in hours and minutes (0:30 + 0:45 should be 1:15, not 0:75).

I.e. it's not necessarily what you want even for groups, so it's important to be explicit about it (@memberwise or similar); fields are a bit special in that it's never what you want--for most other algebraic structures it's at least sometimes correct, but as you noted, we don't have a field abstraction at present.

4 Likes

Makes sense. If there is not a reasonable number of plausible protocols where gated memberwise synthesis would make sense, and it seems like there probably isn't, then I struggle to see the point `@memberwise` here. Just make synthesis kick in for a `MemberwiseAdditiveArithmetic` protocol that inherits from `AdditiveArithmetic` and avoid the marker attribute that doesn't seem to generalise well.

Edit: And `@memberwise` is really just a niche version of `@deriving` or similar which was already suggested, and rejected, for opting in to compiler synthesis of `Equatable`, `Hashable`, etc.