[swift-evolution] [Review] SE-0023 API Design Guidelines (was: Syntax extensions for indicating mutation)

Dave Abrahams dabrahams at apple.com
Fri Jan 29 18:53:38 CST 2016


on Fri Jan 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

>> On 29 Jan 2016, at 20:23, Dave Abrahams via swift-evolution
>> <swift-evolution at swift.org> wrote:
>> on Fri Jan 29 2016, Adriano Ferreira <adriano.ferreira-AT-me.com> wrote:
>> 
>>> Indeed, Ruby has an interesting convention where a question mark
>
>>> (?) is used for boolean functions/methods:
>>> 
>>> In Swift: foo.contains(...)
>>> In Ruby: foo.include?(...)
>>> 
>>> In Swift: foo.isEmpty
>>> In Ruby: foo.empty?
>>> 
>>> Well, in the last case `isEmpty` is a property whereas `empty?` is
>>> a method, but the idea is similar.
>>> 
>>> Also, an exclamation mark (!) is generally used to indicate that a
>>> function/method mutates `self`:
>>> 
>>> Non-mutating:
>>> 
>>> Swift — foo.reverse()
>>> Ruby — foo.reverse
>>> 
>>> Mutating:
>>> 
>>> Swift — foo.reverseInPlace()
>>> Ruby — foo.reverse!
>>> 
>>> Non-mutating:
>>> 
>>> Swift — foo.sort()  or  foo.sort({…})
>>> Ruby — foo.sort  or  foo.sort {…}
>>> 
>>> Mutating:
>>> 
>>> Swift — foo.sortInPlace()  or  foo.sortInPlace({…})
>>> Ruby — foo.sort!  or  foo.sort! {…} 
>>> 
>>> I think it’s a simple and nice way of addressing the naming issue of
>>> mutating vs. non-mutating or pure vs. impure
>>> functions/methods. However, this conflicts with the syntax sugar of
>>> Optionals and, therefore, following this path would have a clear
>>> impact in the language.
>> 
>> Please keep discussion of new language features in a separate thread;
>> thanks.
>
> I thought the point was mostly just to highlight how another language
> has dealt with the same problem? 

Presumably the person posting about this knew what the point was? ;-)

> Of course Ruby’s syntax wouldn’t be feasible in Swift, and at the same
> time I think it’s actually unnecessary to add it so long as a good
> naming convention is very clear about what something is or does, 

Now we're back on topic.

> i.e-
>
> 	foo.sort() // Sorts the original
> 	foo.sorted() // Returns a sorted form of the original, leaving
> the original unchanged
>
> You could do reverseInPlace() instead, but personally I dislike that,
> though the main problem with the above is how easy it is to make a
> typo, whereas the operators for mutability would throw errors if used
> incorrectly. However, if properly declared the mutating vs
> non-mutating methods shouldn’t actually be easy to misuse either, i.e-
>
> 	mutating func sort() -> Void { /* Do some sorting in here */ }
> 	@warn_unused_result func sorted() -> SomeType { /* Make and
> return a sorted copy in here */ }
>
> The first declaration can’t be misused because with its return type
> being void trying to assign it would cause an error, plus the mutating
> keyword ensures that it won’t be intentionally called on a read-only
> value.
> The second declaration’s @warn_unused_result ensures that it won’t be
> misused because it isn’t possible to simply call it, it must be
> assigned or produce a warning. So this actually reinforces that overly
> verbose method names like reverseInPlace() are not needed when
> attempting to convey mutating vs non-mutating behaviour, as the
> compiler will quickly indicate a misuse.
>
> The main problem is when a mutating function also needs to be able to
> return some optional result in addition to mutating itself for
> example:
>
> 	mutating func sort() -> Int { /* Sort self, and return the number of elements that had to be relocated in order to do-so */ }

I don't see a problem here.  We have precedent in, e.g.,
RangeReplaceableCollectionType:

  /// Remove the element at index `i`.
  ///
  /// Invalidates all indices with respect to `self`.
  ///
  /// - Complexity: O(`self.count`).
  mutating func removeAtIndex(i: Index) -> Generator.Element

> However I think the easiest fix in this case would be declare it like:
>
> 	@warn_unused_result mutating func sortAndCountElementsMoved() -> Int { /* Sort and return number of elements moved */ }
> 	mutating func sort() { let _ =
> self.sortAndCountElementsMoved(); } // Call the above, but explicitly
> ignore its result
>
> So eh… in conclusion, I don’t think we need Ruby’s operators as Swift
> can solve the same problems, though maybe not as succinctly.

-- 
-Dave


More information about the swift-evolution mailing list