Why does Numeric require mutating methods?

I'm testing my current knowledge of Swift by writing an immutable Rationals struct:

struct Rational: Comparable {
    let num, den, g: Int
...

and I'd like for it to be Numeric-like (that is, struct Rational: Numeric, but I guess it's called Arithmetic now? [1]). However, according to the documentation, Numeric requires mutating arithmetic operators (+=, *=, etc.).

Since my type is "fundamental" (that is, it's more like an integer or a float than a traditional state-containing data structure), I feel it's better to make it immutable. (The analogy for me is that just as you can't say 5 += 1, you shouldn't be able to say Rational(5,1)+=Rational(1,1)).

Is there any way for my Rational struct to be Numeric-like, or should I abandon this plan? (How do the integers do it?)

[1] http://swiftdoc.org/nightly/protocol/Arithmetic/

1 Like

I would say that it's generally not recommended to make your structs immutable like this (i.e. all the stored properties are immutable), because it's more natural for immutability to be enforced on variables holding Rationals. Note that making your stored properties lets doesn't stop you from mutating the struct, e.g.

struct S {
    let i: Int
    mutating func double() {
        self = S(i: i * 2)
    }
}

var s = S(i: 1)
s.double()

let s2 = S(i: 1)
s2.double() // error: cannot use mutating member on immutable value: 's2' is a 'let' constant

So there's generally no point making a struct where all the stored properties are lets, and you might as well just mutate them directly instead of replacing the whole thing by assigning to self.

As for your analogy, the integer types in Swift are not immutable, e.g.

var i = 1
i += 1 // mutating is fine
Int(1) += 2 // error: left side of mutating operator isn't mutable: function call returns immutable value

and your own type will work the same way, because initialisers aren't l-values (i.e. they can't be directly mutated but they can be assigned to a variable and then mutated). So Rational(5,1)+=Rational(1,1)) won't work, but var r = Rational(5, 1); r += Rational(1, 1) would, as expected.

6 Likes

The mutability doesn't apply to the value itself, but to the variable it's bound to. All the mutable requirements do is allow you to write things like:

var x = 5
x += 1

Also, swiftdoc.org hasn't been updated in a long time. Arithmetic is the old protocol from before Swift 4. Numeric is the current top level protocol for numbers.

1 Like

Is there a similar reference that is up to date? I especially like the type hierarchy diagrams.

3 Likes

integer and floating point types are mutable types. Integer literals are "immutable", but they are not "integers," similar to

If integers were immutable, then var i : Int = 5; i += 1 wouldn't work.

If you trying to define a Rational "literal", then I submit it should not conform to Numeric, since you're defining a literal, like 5, not an analog to an integer type like Int. If you are trying to define a Rational number type that is an analog to an Int, then is should be mutable and then it should conform to Numeric.

I don't believe so. The man who was maintaining swiftdoc.org was hired by Apple to work on the official docs, which, sadly, aren't as comprehensive, even if they are more up to date.