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

Dave Abrahams dabrahams at apple.com
Sat Jan 23 13:53:55 CST 2016


on Fri Jan 22 2016, David Owens II <swift-evolution at swift.org> wrote:

>> Compensate For Weak Type Information as needed to clarify a parameter’s role.
>> 
>> Especially when a parameter type is NSObject, Any, AnyObject, or a
>> fundamental type such Int or String, type information and context at
>> the point of use may not fully convey intent. In this example, the
>> declaration may be clear, but the use site is vague:
>> 
>> func add(observer: NSObject, for keyPath: String)
>> grid.add(self, for: graphics) // vague
>> 
>> To restore clarity, precede each weakly-typed parameter with a noun describing its role:
>> 
>> func addObserver(_ observer: NSObject, forKeyPath path: String)
>> grid.addObserver(self, forKeyPath: graphics) // clear
>
> I don’t understand why to compensate for weak type information we put
> some of that compensation in the name of the function and other parts
> of it in the [external] name of the parameter.

IIUC, some people feel strongly that it would be really strange and
nonuniform to have a substantial fraction of methods with an argument
label on the first argument unless that percentage was nearly 100.

[I don't know whether there's an "official terminology" for these names,
but we've been calling them "argument labels" in the naming discussions.
Note that the ("internal") parameter names aren't strictly internal as
they get used in documentation comments, and should be chosen
accordingly.  So, I'm using the terms "argument label" and "parameter
name".]

> If we were going to reference functions like this:
> addObserver:forKeyPath, then I can understand it. But that’s not the
> plan of record, it’s to do this: addObserver(_:forKeyPath).

That's an interesting argument.

> Regardless of the default naming scheme, it seems like the rule should
> be to use external names to clarify that parameters role.
>
> func add(observer observer: NSObject, forKeyPath path: String)
> grid.add(observer: self, forKeyPath: graphics)
>
> This promotes a very clear and consistent rule: weak type information
> should be clarified by the parameter’s external name. There are no
> exceptions for the first parameter. 

That seems very clean to me.

> Otherwise, it seems like there is super fine line between this rule
> and the next one below.
>
> Additionally, this also alleviates my concerns with the default
> parameter have _ as the external name by default because this
> addresses the case when it would be desirable to have that
> name. 

Sorry, I don't understand what you're getting at here.

> Further, the case below handles the case when it’s not.
>
>> Omit Needless Words. Every word in a name should convey salient information at the use site.
>> 
>> More words may be needed to clarify intent or disambiguate meaning,
>> but those that are redundant with information the reader already
>> possesses should be omitted. In particular, omit words that merely
>> repeat type information:
>> 
>> public mutating func removeElement(member: Element) -> Element?
>> allViews.removeElement(cancelButton)
>> 
>> In this case, the word Element adds nothing salient at the call site. This API would be better:
>> 
>> public mutating func remove(member: Element) -> Element?
>> allViews.remove(cancelButton) // clearer
>> 
>> Occasionally, repeating type information is necessary to avoid
>> ambiguity, but in general it is better to use a word that describes
>> a parameter’s role rather than its type. See the next item for
>> details.
>
> The description here seems to overlap with the “Compensate for Weak
> Type Information” rule, especially with the clause: “repeating type
> information”. It may be better to re-work the example to be
> `removeItem(member: Element)` to make this distinction more clear that
> it’s not type information being removed.

Oh, great point!

> Also, by clarifying that statement, the above rule change I suggested
> would be consistent. Type information clarification goes into the
> external parameter name, functionality clarification goes into the
> function name. Those are hard-n-fast rules that are straight-forward.

I like 'em, FWIW.

>> Be Grammatical
>> 
>> When a mutating method is described by a verb, name its non-mutating
>> counterpart according to the “ed/ing” rule, e.g. the non-mutating
>> versions of x.sort() and x.append(y) are x.sorted() and
>> x.appending(y).
>
> Is this guideline suggesting that we should design our APIs to
> generally have both mutating and non-mutaging counterparts?

Definitely not.

> As other have pointed out, this is also very hard to do all the
> time. I think the alternatives are worse. 

The alternatives to always creating mutating/nonmutating pairs?  What
alternatives have you considered, and what do you see the consequences
to be?

> It would be nice if there were a way to annotate all member functions
> as mutating/non-mutating to really by-pass this ambiguity.

I don't know what you mean by that.  Can you explain?

FWIW, there are potential language-level approaches to this problem
(e.g. https://github.com/apple/swift/blob/master/docs/proposals/Inplace.rst),
but in the absence of language features, it's something we need a
convention for.

-- 
-Dave



More information about the swift-evolution mailing list