[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