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

Tyler Fleming Cloutier cloutiertyler at aol.com
Sun Jan 24 02:09:36 CST 2016


And by CollectionType, I mean Array. It fits the spirit of the API more closely and I wasn’t able to swing mutability with CollectionType.

And…. I should also mention that I just figured out that

> let y = [...]
> let x = y.remainder()
> 
> vs
> 
> var y = […]
> y.remainder()

is insufficient for the compiler to disambiguate. The source of my confusion was that the top example works fine, but the bottom one, not so much. I suppose because although in my example the functions marked mutating return Void, in general mutating functions *can* return a value.

So perhaps, as suggested, InPlace or some symbol could be used to disambiguate. 

Tyler


> On 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 <mailto:swift-evolution at swift.org>> wrote:
>> 
>> 
>> on Fri Jan 22 2016, Joe Groff <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>>>> On Jan 22, 2016, at 1:57 PM, Jeff Kelley via swift-evolution
>>>> <swift-evolution at swift.org <mailto: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>
>>> 
>>>>> <mailto:swift-evolution at swift.org <mailto: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 <mailto: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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160124/a1e81556/attachment.html>


More information about the swift-evolution mailing list