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

David Owens II david at owensd.io
Mon Jan 25 13:12:17 CST 2016


> On Jan 24, 2016, at 7:07 PM, Dave Abrahams <dabrahams at apple.com> wrote:
> 
> Understood; the "sentence-like" idea keeps getting repeated.  The
> keepers of Cocoa APIs tell me that, since several years ago, they moved
> away from adding prepositions to secondary selector pieces to make
> things more sentence-like.

Yes, I’ve noticed that as well. 


>> The problem I see is that there is a mixed bag of what "good" APIs
>> look like. And when push comes to shove, code wins. The libraries
>> exposed in code, even through the ObjC bridging, will be used as the
>> basis of what determines what good Swift APIs look like more than a
>> published doc.
> 
> Yep.  But a big part of the reason to publish the doc is so we can
> decide what direction to push the library APIs.

I agree. And really, I find most of the doc fairly good.

>>> The reason "for" is there is that otherwise you can't tell what the
>>> relationship between the observer and the keyPath is.  For example, if
>>> it had been "at" instead of "for," it would completely change the
>>> meaning. "Of" would probably be more descriptive, frankly.  But the
>>> example isn't trying to illustrate anything about that preposition or
>>> that relationship.  
>> 
>> Right, but the argument is that the API is indeed better with the
>> "for" preposition. I believe it is, I think you are also saying that
>> it is.
> 
> Yes.  So... what are we arguing about?

At the heart of it, that the guidelines don’t seem to help one down a path to use prepositions, but rather only descriptive nouns. 

So in one sense you are arguing to make things more English-like because they read better at the call site, but on the other, you’ve stated APIs reading more English-like is a non-goal. Basically, I think the guideline tries to give some rules on how to get get APIs, but doesn’t talk much about what a good API is.

>> This is the closest guideline that I think attempts to address it:
>> 
>>> Clarity at the point of use is your most important goal. Code is
>>> read far more than it is written.
>> 
>> But the clarity here is, ironically, inherently ambiguous. Clarity
>> could mean to make the weak type information known. Clarity could be
>> about intention of how the parameter could be used. Clarity could
>> simply be a more verbose name that provides additional context. I
>> think it would be clear to have guidelines that actually describe what
>> you think make up a good call site.
> 
> We intend that to mean "clarity of semantics."  Would that change, um,
> clear this up for you?

Maybe so.

> 
>> thing.strideTo(10, stride: 1) // clear
>> thing.strideTo(10, by: 1)     // clear and linguistically better
>> thing.strideTo(10, 1)         // not clear
> 
> Actually I think the first is unclear simply because the word "stride"
> takes two different roles, one as a noun and one as a verb.  It's
> possible to misinterpret the 1 as being an expression of the word in the
> base name, or at least it's possible to be confused by that until you
> work out all the reasons that it doesn't make sense.
> 
>> Both provide some amount clarity at the call site as the value of 1 is
>> quite clear what it means. So in one sense, I agree that the defaults
>> of the language today push you towards clarity.
>> 
>> To me, the guidelines, as I understand them, lead us to the first
>> option when I believe the intent is to actually get to the second
>> usage.
> 
> Yes, IMO that is the intent!  But why do you think they lead us to the
> first option?  Because they explicitly mention using nouns to clarify
> roles?

Yeah. I don’t think you actually mean descriptive nouns.

>> However, indeed someone thinks that we should use "at", but based on
>> the guidelines, it's really hard to understand why:
>> 
>> -  mutating func insert(newElement: Iterator.Element, atIndex i: Int)
>> +  mutating func insert(newElement: Iterator.Element, at i: Int)
>> 
>> Again, I agree that "at" reads better at the call site. However, it
>> doesn't provide any real clarity over the default "index" (sidenote: i
>> is still a bad choice ;)), 
> 
> It all depends on what it does for the doc comment.  IMO there's nothing
> wrong with “Inserts `newElement` before the `i`th element, or at the end
> if `i == count`”.

Hmm… we disagree on that point. If I need to go the documentation to get basic clarification of the parameter, I’m not really sure what the point of the label is then.

>> I actually really hard pressed to come up with a definitive example as
>> to why that should be allowed. The best I can argue is that "add"
>> itself is too generic and could be confused on the implementing type
>> that may also have an "add" function. However, the additional label
>> information helps with that.
> 
> You have to consider how this protocol is going to be used; it's a
> mix-in that will be broadly applied to many things that could add an
> NSObject for many purposes.  Unfortunately, as hard as we tried, we were
> unable to come up with guidelines that actually make API design easy!

Agreed. API design isn’t easy. =)

>> However, I absolutely believe that the "strideTo/strideThrough"
>> example is clearly on the side of really being part of the argument
>> label. In both cases we are striding, the difference is on where we
>> stop.
>> 
>> extension Strideable {
>> 
>> -  public func stride(to end: Self, by stride: Stride) -> StrideTo<Self>
>> +  public func strideTo(end: Self, by stride: Stride) -> StrideTo<Self>
>> 
>> }
>> 
>> extension Strideable {
>> 
>> -  public func stride(through end: Self, by stride: Stride) -> StrideThrough<Self>
>> +  public func strideThrough(end: Self, by stride: Stride) -> StrideThrough<Self>
>> 
>> }
>> 
>> This change seems to be simply because the usage of labelled first
>> parameters are frowned up, not because it's actually a better place to
>> describe what is going on.
> 
> I can't argue with you here.

So the question is this though: what is the better Swift API? I think examples like these and reasons as to why one is chosen over the other at least help with the guidelines being more inclusive. I agree that it’s very hard to generalize something as subjective as a “good API”. So in light of that, I think more examples of some of the boundaries edges at least give people some reference to compare ask, “is my API more like this or more like that?”

>> I'm trying to, but we're not really following the ObjC guidelines
>> either. At least on paper. I think you keep talking about readable
>> APIs, but I feel the guidelines keep talking more about
>> descriptive. 
>> 
>> The difference is subtle, I agree. But I think it's the difference
>> between these two APIs:
>> 
>> items.insert(12, atIndex: 2)  // or items.insert(12, at: 2)
>> 
>> items.insert(12, index: 2)
>> 
>> Again, it's about which is supposed to be "canonical" Swift.
> 
> 
> OK, I'll try to come up with some wording adjustments that sort this out.
> 
> Thanks for your patience,

Thanks for being willing to listen and discuss!

-David


More information about the swift-evolution mailing list