[Accepted with Modifications] SE-0246: Generic Math Functions

Hi, everybody. The review for SE-0246: Generic Math Functions ran from March 11th through the 25th. This review was extended several times in order to make some adjustments in response to feedback gathered during the review. The core team would be interested in getting feedback from reviewers on how they felt about this rapid iteration, and in particular on whether they felt it detracted from the process overall.

Feedback was positive on the general idea of providing a standard way of accessing this functionality. Some suggestions were made that the authors agreed with and chose to immediately incorporate into the proposal, leading to extension of the review period. The main suggestions that weren't incorporated into mid-review revisions were as follows:

  • Several people expressed unhappiness that log meant the natural logarithm (base e). Unfortunately, as observed in the review, the most obvious alternative name for the natural logarithm is ln, which is not a good programming name: it's too short and too visually similar to keywords like in. log for the natural logarithm is also extensively precedented in many programming languages, including C, Swift's most obvious antecedent (and the way that people have gained access to this functionality in Swift before this proposal). Ultimately, the core team believes that sticking with log is the best choice even if there's some risk of people using it when they meant log10 or log2.

  • There was a suggestion that create a Math module might create conflicts with names used by other libraries today. The core team agrees that this is an issue which needs further study and work, but decided to define it away for this proposal by simply merging the proposed Math module directly into the Swift module, which already has some similar math functions (such as min and floor); there didn't seem to be a strong motivation for separating out the functions proposed here.

Therefore, SE-0246 is accepted with the modifications that the Math library should not be added and that the proposed new entities in it should instead be added directly to Swift. If the community finds this worthy of further debate, the core team is willing to put SE-0246 with these modifications up for further review; just let me know, either in this thread or privately via email or direct message.

As always, thank you for helping to make Swift a better language.

John McCall
Review Manager

21 Likes

I thought this worked well, and enhanced the process instead of detracting from it. The alternatives here seem to be either sending the whole proposal back for revision (very heavyweight, wastes a lot of people's time) or having the core team accept with similar modifications (wouldn't allow reviewers to respond to the changes). Given that the on-the-fly changes didn't fundamentally alter the core concept of the proposal, this seems to combine the benefits of both those options.

6 Likes

I don't feel like this is a very good argument. in as a keyword is only found in the context of a for-in loop and function argument labels. ln will almost always be found in the context of a function call with parentheses, ln(x) vs f(in: x).

Apart from this, most of the time, I've needed to use ln is inside fairly large very 'mathy' equations where it's very obvious I'm not using in.

Yes, but we aren't changing the meaning of log to be something else, we'd just be renaming it. Precedence is certainly an important aspect to consider and we should not deviate lightly. Though we've made changes with strong precedent before because it can cause confusion for some (post and pre-fix ++/--).

Swift also prides itself on a learning language which means we should be expecting more people newer to programming to be joining Swift. We should do what we can to help to translate learning material to code. Certainly, no one completely new is going to need logarithmic functions in their first week, but they will eventually.

If someone writes log expecting it to be base 10 it can be a very hard bug to track down. This is usually because you're calling log inside of a larger expression with many other moving parts. It's much safer to remove any possible ambiguities if we can.

9 Likes

@scanon, did we ever seriously consider logE as an alternative?

11 Likes

AFAIK it was never floated on the review thread, so no. That's the first time I've seen it mentioned as a suggestion. But, given that log as the natural logarithm is nearly universal in programming languages (not only the C family; literally the only languages I found that do something else are Rust, Kotlin, and some Pascal variants), and ln is the ISO-endorsed spelling, I think the burden to consider anything else should rightly be extremely high to ensure that we aren't doing something different just for the sake of being different.

Adopting the ISO spelling (ln) would also be problematic, because that would lead us to lb and ld instead of log2 and log10, and those names are significantly less clear.

So, basically, I think this issue is a mess all around, there isn't a good solution, and keeping log is both the simplest and leads to the smallest amount of source-churn, so it's ultimately correct even if unsatisfying.

19 Likes

Furthermore, anyone who prefers the ln spelling can write that function themself, and the implementation is so trivial that there’s no chance they would get it wrong.

3 Likes

Well, no. The objections seem to be to the presence of log, not the absence of ln.

4 Likes

I think it looks and feels amateurish. Put it another way: do you think the C++ standards committee would accept a proposal in such a way?

I don't think any of us in the community would get away with that. Let's be honest: this was accepted despite the numerous revisions because the author works at Apple and likely speaks to most of you face-to-face. Having a lower bar for contributions by Apple employees than for the rest of us doesn't reflect well on the project - or, to be frank - on Apple. One would hope you take more care when designing your own APIs.

It is also disrespectful to community members. We do not have infinite time to evaluate and re-evaluate proposals. When they are put up for review, they should be ready for review.

Even the version you posted here is still not correct. It still mentions the Math library which has apparently been removed. (EDIT: Ok, the document does mention this. But my other criticisms remain.)

During the review thread, I noticed a lot of questions were left open and simply punted to the core team. How am I, as a reviewer, supposed to evaluate such a proposal? Again: would you accept that from a community member?

During the review, I had (and continue to have) serious misgivings about the names ElementaryFunctions and Real as well as the .Math pseudo-namespacing. Then the proposal changed and removed the namespacing, but left the protocol names. Am I supposed to restate my objections? I wasn't sure, and to be honest I had too many other things to do. See the point above about not having infinite time.

10 Likes

I'm kind of split on the iteration process. On the one hand, I want the language to be able to evolve quickly, and I'm okay with Swift core team members, Apple employees, and other major contributors being able to fast-path changes provided they do consider feedback (which happened here).

On the other hand, the review thread for this proposal felt more like a pitch thread. I wonder if some changes to the review process might be useful; for instance, to be able to withdraw a proposal back to pitch stage. At the risk of introducing even more friction into the process, it would also be useful to break a proposal into different subtopics; often, a largely uncontroversial proposal is held back by minor controversial aspects, and it is useful to accept (and remove from the discussion) the parts of the proposal that reach consensus and only have continued debate on the controversial parts. Having a more formalised process for that may be beneficial.

Just throwing ideas out there: I feel like the tools (i.e. linear threads on forums) are holding us back somewhat. I can imagine a tool wherein the community could mark a proposal line by line. For example, if we could give a tick/cross to different lines in each proposal as a sort of community sentiment metric that could help narrow the discussion. I also feel a restriction of one post per person in the initial review thread would be useful, responding only to the proposal text and not to other users (but with edits allowed). Once we have the community sentiment and individual comments, the largely-uncontroversial components (if any) would no longer be open for debate and a second review thread could take place to lock down the remaining details.

3 Likes

I prefer logn()

1 Like

I shouldn't have mentioned logE; I think it unfortunately led people to think that we were considering new names for log. The review is closed.

4 Likes

Maybe we should have an evolution sub-forum for 'indicative votes', indicating what the community does/does not like (despite the link, it's a serious suggestion, if we think it would result in better proposals for review).

I think this proposal should have gone through another round. Even if it was just a formality with everybody saying "+1".

I wonder as well about how easy it is for the core team to collate the feedback when some of it is for revision A and some refer to issues with revision B which may have been partially addressed in revision C. If I was presented with that kind of feedback, I'm not sure how I would combine it to figure out the overall community sentiment for revision D. It's just more difficult for everybody if the target is moving and shape-shifting.

We should have respect for everyone's contributions, proceeding with the starting point that everyone has limited time, and any feedback given once should be sufficient to make that point of view heard and should be taken to apply to all iterations of the proposal.

That said I don't think this community is even close to being representative of Swift users in general; therefore, it's always important to keep in mind that this process is not a vote, and "+1" comments are not encouraged. As a community we do not approve or reject proposals: we review them and our comments are considered in the core team's decision.

25 Likes

These seem directly at odds with each other:

The proposal already went through a pitch phase. Additional concerns were raised during the review phase. I'm having trouble working out how you would have preferred them to be addressed.

I also find these accusations of bias somewhat rude, and I'm not sure they are well-founded. Do you have examples of non-Apple authors being treated differently here? This isn't the first time (very) minor changes have been made to a proposal during review, and the review period was extended to give everyone more time to respond.

Just to amplify the precedence argument for "log", the following languages use "log" to mean natural logarithm:

Fortran
C
Matlab (scripting language)
Python
Mathematica (actually "Log")

Of course there are many other examples (Ada, anyone?) but I think most of the mathematical and engineering oriented code written since, well, the invention of high level languages has probably been written in one of the languages listed above.

I think it is unlikely that defining "log" as natural logarithm will cause much confusion.

2 Likes

Right. But this is only true for people that have been programming and are new to Swift. This is not true for people who are new to programming and new to Swift.

My argument for confusion, although it's more like an argument against a subtle bug, is that: People who confuse log for something like log10 will have a subtle bug in their code that's hard to debug because it's a semantic error.

I understand that there's a lot of history in programming languages and there will certainly be a lot of annoyance from people who aren't new to programming.

But I see this as a net gain. Some people, possibly more, getting annoyed because log has been renamed, versus newcomers, spending time trying to find a very subtle bug because the function they know from a textbook is something else in the language.


But all this said it looks like the decision is made. And I can't say that I'm unhappy with the overall result of the proposal. Thanks, @scanon I can finally remove all of my import Darwin's :slight_smile:

1 Like

That's why you write tests; using the wrong base for a logarithm doesn't give you "weird edge case bugs", it just breaks the behaviour altogether, so you'd very likely catch that. Also, I would assume that the people that aren't aware that there are multiple bases for logarithms, probably also aren't the people that would know what a logarithm is in the first place and are therefore unlikely to reach for one.

And the "textbook argument" could really go either way, because computer scientists will generally use "log base 2", physicists might use "log base 10" and mathematicians generally "log base e".

Not if you're trying stuff out, writing a simple little script or even just playing around.

When it's used in a larger context it's harder to find because it does give you a 'valid' result which may be subtly different from what you expect.

I'm saying that log is commonly used to refer to a different base to different people. I've only seen log used as log2 for complexity, everything else has used log to refer to log10 for example.

I was simply arguing to remove this ambiguity for everyone by making it explicit which log a person is referring to. I don't even care that much we call log ln instead, just so long that it's not ambiguous.

1 Like

I apologize for being late to this, but I'd very much appreciate more detail on how this affects source compatibility. Given that these new functions are gated to Swift 5.1 and Darwin functions are deprecated, does this mean I will be required to include @available wherever I use math functions in iOS for the next 3-4 years just to support iOS 12.2 for that period?

No.

First off, deprecated is not the same as removed or obsoleted, so you will not have to move away from using the Darwin functions yet anyway.

More importantly, part of dropping the Math module is that these functions will not be gated to 5.1. They are (with one exception) unconditionally available. You can just use them. If you have imported the Platform module (or more likely Foundation), you will end up with the definitions there in some cases, but your sources will work without needing to do any @available hacks.

The only exception to that is sqrt, because the generic constraint for the free function changes from FloatingPoint to ElementaryFunctions. For concrete uses this will not matter; if you are using swift < 5.1, you get the old one, if it's >= 5.1 you get the new one. Code that is generic on FloatingPoint and compiles with swift >= 5.1 will either need to switch to being generic on Real (which you probably want to do anyway) or use .squareRoot() or .sqrt() instead. This is an annoyance, but hopefully an extremely minor one.

At some point (maybe 5.1, maybe later, this is still being discussed somewhat), we will start obsoleting existing math functions in the Darwin module. When this happens, if you are using fully-qualified names like Darwin.acos(x), you will need to use the new stdlib names (Swift.acos(x) or acos(x)), but again, very few users should hopefully find themselves in this situation. Or if we get the ability to reexport specific interfaces from another module, we may never need to do this.

8 Likes