[swift-evolution] [Review] SE-0023 API Design Guidelines

Dave Abrahams dabrahams at apple.com
Tue Jan 26 12:43:30 CST 2016


on Sun Jan 24 2016, Jacob Bandes-Storch <swift-evolution at swift.org> wrote:

> Good/important examples.
>
> The problem here seems to be that "sin", "mean", "remainder", etc. read
> nicely as noun phrases, but the guidelines would recommend -ed/-ing verb
> forms in this case (methods with both mutating and nonmutating variants),
> not noun phrases:
>
>     "*When a mutating method is described by a verb, name its nonmutating
> counterpart according to the 'ed/ing' rule*."
>
> ...but we don't have a verb to which to apply -ed or -ing. ("sined"?
> "meaned"? "remaindered"?)
>
> I agree that "sinInPlace()", "meanInPlace()", "remainderInPlace()" seem to
> work pretty well, but I'm aware that I'm drawing on experience with the old
> names of sortInPlace(), subtractInPlace(), etc., and in fact this case is
> worse because the first words aren't really verbs (you can't sine something *in
> place* any more than you can sine it). "takeSine()" doesn't sound great
> either.

Nope, InPlace becomes a term-of-art in these cases.
x.replaceWithTheResultOfApplyingSin() is linguistically correct, but too
unwieldly to be a serious contender.

We'd be very happy to have a better alternative than InPlace for these
cases, but so far, we haven't found any.

> One option is to simply not offer mutating versions of these methods; then
> users can write "y=y.remainder()" with only two extra characters (four with
> spaces). IMO this is awkward; it also kinda necessitates an
> isUniquelyReferenced check to avoid unnecessary copying.

Unfortunately, an isUniquelyReferenced check can't actually solve the
problem.  The language semantics force an additional reference going
into the union method when you write

     x = x.union(y)

It's also useless for scenarios like

     a[x] = a[x].union(y)

> Jacob "no useful suggestions to see here" Bandes-Storch
>
> On Sat, Jan 23, 2016 at 11:36 PM, Tyler Fleming Cloutier via
> swift-evolution <swift-evolution at swift.org> wrote:
>
>> This is a very real problem. It has come up a nontrivial number of times
>> during my attempt to conform Surge <https://github.com/mattt/Surge> to
>> the API Guideline. My work in progress version is here
>> <https://github.com/TheArtOfEngineering/Jolt>.
>>
>> Surge is in essence Swift a wrapper around the Accelerate framework.
>> Originally the Surge API was constructed as a collection of global
>> functions that mirrored the Accelerate framework exactly. I’m currently
>> attempting to bring the API in line with the API Guideline and to extend
>> CollectionType to have methods with these operations.
>>
>> Thus I have three different types of API end points.
>>
>> - Global functions
>> - Non-mutating methods
>> - Mutating methods
>>
>> These distinctions could potentially be important given that these are all
>> performance critical vector operations. Also given that this is extending
>> the Accelerate framework many functions are named in mathematical terms,
>> which makes them more difficult to name according to the current
>> guidelines. Consider the following functions.
>>
>> First let’s consider a function like `add` which operates on vectors and
>> whose output is also a vector. This function is straightforward to conform
>> to the API Guidelines, the result of which is quite satisfying.
>>
>> The global function is called `add`, the mutating method is called `add`,
>> and the non-mutating method is called `adding`.
>> add(x, y) vs x.add(y) vs x.adding(y)
>>
>> Excellent.
>>
>> Next, however, consider the vector function `sum`. In this case the global
>> function is named `sum`. There is no corresponding mutating method for
>> `sum` since it does not output a vector. Should the non-mutating method
>> therefore be called `sum` or `summed`? The Guideline is not so explicit
>> about this. You could conceivably make an argument for either, although I
>> think `sum` would win out because there is no non-mutating version. If
>> there is no current mutating version for an API, but there could
>> conceivably be one in the future, I assume that it should use the ‘ed/ing'
>> rule.
>>
>> sum(myVec) vs myVec.sum() // <- this is non-mutating
>>
>> So far, acceptable.
>>
>> What about the function `sin`?
>>
>> Again, the global function is just `sin`. How, though, do you distinguish
>> the mutating and non-mutating methods?
>>
>> sin(x) vs x.sin() vs x.sined()?
>>
>> Fortunately, the API Guideline does have a comment on this. In the case of
>> `sin`, it is reasonable to only provide the global function in accordance
>> with mathematical convention. The problem is that memory constricted
>> environments may require that the calculation be done in place. What that
>> should look like is not clear.
>>
>> What about functions that are almost always abbreviated by convention like
>> `ceil`?
>>
>> ceil(x) vs x.ceil() vs x.ceiled()
>>
>> In this case the InPlace prefix can save us. x.ceilInPlace() would work.
>>
>> Another good one is `remainder`.
>>
>> remainder(x) vs x.remainder() vs x.remaindered() ?
>>
>> These are just a few examples, but there are many many others in this API
>> alone that the Guidelines struggle to address. Most are problematic for the
>> same reasons outlined above. If it weren’t necessarily critical to reduce
>> memory use it would suffice to just include the global function for many of
>> them.
>>
>> e.g.
>> reciprocal
>> threshold
>> floor
>> abs -> ???
>> exp -> x.exponentiated()?
>> exp2 or expSquared
>> logb
>> mean
>> dot
>> cross
>>
>> For some functions like `dot` we could conceivably rely on the fact that
>> familiar users would know that the return type is not the same as the
>> arguments. I would argue however, that, if we were going to rely on the
>> type information of the returned value then perhaps that’s what should be
>> relied on exclusively to determine whether a method is mutating or not.
>>
>> All mutating methods should return Void and all non mutating methods
>> should have @warn_unused_result. Thus the context would distinguish it’s
>> mutability. Not a huge fan of this, but with the unused warning result it’s
>> not too bad.
>>
>> For example.
>>
>> let y = [...]
>> let x = y.remainder()
>>
>> vs
>>
>> var y = […]
>> y.remainder()
>>
>> I hope this helps to give a little bit of color on a real world example
>> outside of the standard library.
>>
>> Thanks,
>>
>> Tyler
>>
>>
>> On Jan 23, 2016, at 11:00 AM, Dave Abrahams via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>
>> on Fri Jan 22 2016, Joe Groff <swift-evolution at swift.org> wrote:
>>
>> On Jan 22, 2016, at 1:57 PM, Jeff Kelley via swift-evolution
>> <swift-evolution at swift.org> wrote:
>>
>> On Jan 22, 2016, at 4:53 PM, Joe Groff via swift-evolution
>> <swift-evolution at swift.org
>>
>>
>> <mailto:swift-evolution at swift.org
>> <swift-evolution at swift.org>>>
>> wrote:
>>
>> How do you handle naming non-mutating versions of these operations?
>> Conjugating other irregular verbs also imposes a barrier on
>> developers whose first language is not English.
>>
>>
>> Swift could use “after” as a prefix, or something similar.
>>
>> array.sort()
>> array.afterSort()
>>
>> I was tempted to say “afterSorting()” but that has the same problems
>> mentioned above.
>>
>>
>> That's reminiscent of the way the classic Cocoa naming guidelines
>> cleverly avoided these issues by using the 'did-' prefix consistently
>> instead of ever conjugating verbs into preterite tense. 'after' is a
>> bit awkward, though, as are any other equivalent prefixes i can think
>> of that have the same effect on the past participle (havingSplit?
>> bySplitting?)
>>
>>
>> "splitting" works perfectly well on its own, IMO.  If there's an
>> argument to the method, you'll want some kind of preposition like "At"
>> or "On" afterwards.
>>
>> I think the real problem cases for this guideline as written are the
>> ones where there's no underlying verb, like "union", or where the verb
>> already has a strong non-mutating connotation, like "exclusiveOr."
>> Those are the ones that really cry out for a different convention, like
>> "InPlace."
>>
>> In developing the guidelines, we had some internal arguments about
>> whether it was worth adding complexity to accomodate those cases, or
>> whether we should simply not mention them and let them get sorted out on
>> a case-by-case basis when they come up in code review.  We didn't reach
>> consensus, so this would be a useful area in which to get community
>> input.
>>
>> --
>> -Dave
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-- 
-Dave



More information about the swift-evolution mailing list