Skip to content

Conversation

@stephentyrone
Copy link
Member

For now, this mostly allows one to write code generic over Real and Complex types. TBD whether it should also include finite fields, or be restricted to characteristic-zero. Also bikeshedding to be done on exactly what operations should fall into this protocol and what it should be named. This is just to get discussion started.

For now, this mostly allows one to write code generic over Real and Complex types. TBD whether it should also include finite fields, or be restricted to characteristic-zero. Also bikeshedding to be done on exactly what operations should fall into this protocol and what it should be named. This is just to get discussion started.
@stephentyrone
Copy link
Member Author

CC @airspeedswift Thoughts on naming? Field is obvious and correct, and mostly unlikely to get confused for some non-math term, but we deliberately avoid Ring and CommutativeGroup for Numeric and AdditiveArithmetic, for example.

  • Real and Complex are only approximately fields (because of rounding); we plausibly might want to reserve the name Field for future things that are really exact fields, and use ApproximateField or some less-technical name here (but a good one hasn't yet come to me).

  • We could alternatively pick division rings as the thing to give a name to now; the difference between a field and a division ring is that a field must have a commutative multiplication. The practical difference in use is that quaternions are a division ring but not a field. Do people have good use cases for code generic over quaternions and the real numbers, for example?

@NevinBR
Copy link
Contributor

NevinBR commented Jan 15, 2020

Brainstorming the bikeshed:

Arithmetic because the protocol requires all four basic arithmetic operators (+, -, *, /). This might sound too much like something integers would conform to though.

Dividable because it is for types whose values can be divided. A bit strange as a word, but perfectly cromulent. Also could seem to imply integer conformance though.

Fractional is technically correct, thought it might give the mistaken impression that only real numbers can conform.

Field is good, and not too onerous for non-mathematicians, though as mentioned it takes the “good” name away from a potential true field protocol where division is exact and reversible, and it implies commutative multiplication.

DivisionRing is extremely precise, but I think it is too technical for the general user-base. If we want the name to mention dividing, then I think Dividable is much more readily understood by non-mathematicians.

• • •

Regarding commutativity, on one hand it would seem odd to arbitrarily exclude quaternions from such a basic protocol. On the other hand, it is extremely likely that people will write algorithms against this protocol that implicitly assume multiplication is commutative, because most people won’t even think about the possibility of it being otherwise.

It might be beneficial to require commutativity in the protocol, so that people working with quaternions don’t get caught off guard by such algorithms. Of course, then people might provide their own retroactive conformance extension Quaternion: Field {} and get incorrect results…

@NevinBR
Copy link
Contributor

NevinBR commented Jan 15, 2020

It occurs to me that Numeric does not require commutative multiplication. Since quaternions would definitely conform to Numeric, it follows that the “danger” of algorithms-which-implicitly-assume-commutativity already exists. Any such algorithm that doesn’t involve division could be written against Numeric, and would not work correctly for quaternions.

The new protocol does not create this problem. The problem already exists.

@stephentyrone
Copy link
Member Author

stephentyrone commented Jan 15, 2020

Right, Numeric deliberately didn't require commutative multiplication because we thought it was more useful to allow square matrices and quaternions to conform. This isn't really a "problem" per se; it's a deliberate design choice.

It's actually fairly hard for non-experts to get into too much trouble with Numeric allowing non-commutative multiplication though, because for simple use, it tends to only cause a problem when people start multiplying by inverses to cancel expressions (and multiply on the wrong side), and there's no way to get an inverse out of the Numeric protocol. It can also cause a problem when people re-arrange large expressions, but when you don't have inverses there's rarely a good reason to do that in a generic setting. It's worked out OK so far.

My question is really just "should this thing include quaternions or not", because quaternions are the only finite-dimensional thing that would be added in relaxing the conformance to division rings--all finite division rings are fields, and the only division rings that are finite-dimensional algebras over the reals are the reals, complex, and quaternions. On the other hand, this would exacerbate the non-commutativity of multiplication somewhat, because in this setting, you do have inverses and people are likely to apply them on the wrong side.

@NevinBR
Copy link
Contributor

NevinBR commented Jan 16, 2020

My question is really just "should this thing include quaternions or not", because quaternions are the only finite-dimensional thing that would be added in relaxing the conformance to division rings--all finite division rings are fields, and the only division rings that are finite-dimensional algebras over the reals are the reals, complex, and quaternions.

…what, you mean you don’t expect people to model noncommutative finite-dimensional algebras over ℚ in the Swift type system? :-p

But yeah, point taken.

There are certainly some algorithms that make sense for the noncommutative case, for instance taking the average of a sequence:

extension Sequence where Element: Fieldish {
  func average() -> Element {
    let (sum, n) = self.reduce((0, 0)) { ($0.0 + $1, $0.1 + 1) }
    return sum / Element(n)
  }
}

• • •

Coming at this from another angle, are there any important algorithms which require commutativity?

And are there any compiler optimizations which can be enabled through commutativity?

@stephentyrone
Copy link
Member Author

Coming at this from another angle, are there any important algorithms which require commutativity?

There aren't a lot of things that require commutativity, but there are a lot of things that admit a faster implementation on a computer if you can assume commutativity. e.g. computing the product of an array of numbers; if I can assume commutativity (and I am prepared to accept small rounding perturbations, and assume that intermediate overflow isn't an issue), I can have two accumulators and run all the evens into one and all the odds into the other, then multiply the two together. This doesn't reduce the asymptotic complexity, but it reduces the length of the dependency chain, and can be a significant latency win. It's not a huge deal, but such things do exist.

And are there any compiler optimizations which can be enabled through commutativity?

We don't (yet?) have any means to communicate to the compiler that an operation is commutative (it knows a priori about the commutativity of the operations in the Builtin module, but we don't have a way to add that information to an operator defined in Swift code).

@stephentyrone
Copy link
Member Author

I think that I've talked myself around to requiring commutativity for this protocol, because as we start adding linear algebra operations, I would like to initially avoid the complexity of left- and right-modules. So let's focus on names for Field-like.

/// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil for `Real` or
/// `Complex` types, because these types have an `.infinity` value that acts as the
/// reciprocal of `.zero`.
var reciprocal: Self? { get }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the rationale for making the reciprocal property Optional?

For comparison, the unary-negation operator in the standard library is non-optional and traps on overflow (eg. -Int.min).

Copy link
Member Author

@stephentyrone stephentyrone Jan 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditions under which unary-negation would trap are trivially detectable a priori, so a trap is an appropriate error mechanism. The conditions under which multiplying by a reciprocal would deliver a result that is either incorrect or has significantly reduced accuracy are significantly harder to characterize, and users are likely to either fail to detect them, or get them wrong. Returning an optional lets us handle the check for them.

Copy link
Member Author

@stephentyrone stephentyrone Jan 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This glosses over the fact that detecting when unary-negation would trap in a protocol context is non-trivial for all the named protocols in the hierarchy under SignedNumeric, so we probably should add an optional negated property at some point.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s actually really clever. People who want the trapping behavior can write 1/x, and people who want to check for failure can use reciprocal.

+1 to this design, and +1 to bringing it to the standard library for negated as well.

@NevinBR
Copy link
Contributor

NevinBR commented Jan 16, 2020

I think that I've talked myself around to requiring commutativity for this protocol, because as we start adding linear algebra operations, I would like to initially avoid the complexity of left- and right-modules. So let's focus on names for Field-like.

Works for me, I vote Field. It’s short, to-the-point, and conveys the right meaning.

We might walk this back in the future, but for the short-term let's avoid stomping on the generic term "Field".
@stephentyrone stephentyrone changed the title Introduce Field protocol Introduce AlgebraicField protocol Jan 19, 2020
@stephentyrone stephentyrone marked this pull request as ready for review January 19, 2020 20:55
@stephentyrone
Copy link
Member Author

After discussion with the rest of the standard library team, I've changed the name to AlgebraicField to hopefully make it a little bit less ambiguous and more discoverable for people unfamiliar with the abstraction. No one had very strong opinions either way, so it's entirely possible that we'll end up adding the Field name in the future and deprecating the more verbose alternative, but let's start with the more explicit name.

/// adding division.
///
/// A field is a set on which addition, subtraction, multiplication, and
/// division are defined, and behave basically like those operations on
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// division are defined, and behave basically like those operations on
/// division are defined, and behaves basically like those operations on

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Behave" is correct here; the subject is "addition, subtraction, ... and division".

@NevinBR
Copy link
Contributor

NevinBR commented Jan 20, 2020

After discussion with the rest of the standard library team, I've changed the name to AlgebraicField to hopefully make it a little bit less ambiguous and more discoverable for people unfamiliar with the abstraction. No one had very strong opinions either way, so it's entirely possible that we'll end up adding the Field name in the future and deprecating the more verbose alternative, but let's start with the more explicit name.

I’m not convinced the longer name is actually clearer.

To the vast majority of people, “algebra” means high school Algebra 1 & 2, and they have no idea that abstract algebra even exists. If they have encountered anything “algebraic” before, it is only the algebraic numbers. To these people, Field and AlgebraicField are equally uninformative, and the longer name just adds length.

And to people who are familiar with abstract algebra, the term “algebraic field” probably makes them think of the field of algebraic numbers. To these people, Field by itself conveys the proper meaning, and the longer name makes them wonder if something more esoteric is involved.

@xwu
Copy link
Contributor

xwu commented Jan 20, 2020

I think AlgebraicField reads well.

Naked Field could probably never migrate to the standard library as is; the much more common usage of the term is in contexts such as TextField orSecureField. One can imagine a protocol generalizable over all UI fields named Field, and it would be very confusing to have Swift.Field and SwiftUI.Field.

@stephentyrone
Copy link
Member Author

stephentyrone commented Jan 20, 2020

I think AlgebraicField reads well.

Naked Field could probably never migrate to the standard library as is; the much more common usage of the term is in contexts such as TextField orSecureField. One can imagine a protocol generalizable over all UI fields named Field, and it would be very confusing to have Swift.Field and SwiftUI.Field.

This is basically the feedback I got from people I talked to about the issue. "Field" is very natural to us as mathematicians, but for someone who is not from a math background, "Field" doesn't even suggest that it's a mathematical concept that's being described. "AlgebraicField" is not perfect, but it gives a clue to the uninitiated that it's describing a "math thing" (and Google will lead them to the truth).

One option to resolve this in the longer term would be to introduce an "abstract algebra" module and put all of the formal structures, as well as the conformances, into that module, where they wouldn't bother people who just want to use complex numbers or generic math functions. This is useful in the meantime.

@stephentyrone stephentyrone merged commit 7bd2909 into master Jan 20, 2020
@stephentyrone stephentyrone deleted the FieldProtocol branch January 22, 2020 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants