Swift-style-guide: Functions vs. Methods

Created on 6 Jul 2015  ·  26Comments  ·  Source: raywenderlich/swift-style-guide

The guide should be explicit about the appropriate use of function and method when writing. Having taken a look at Apple's Swift Programming Language docs, something like:

A method is a function that's associated with a class, struct, or enum. This goes for both instance and type methods. A function, on the other hand, is declared in the global scope and doesn't belong to any type.

would be sufficient.

Most helpful comment

How about something like this:

Methods vs Free Functions

Methods are functions associated with a type and preferred because of their autocomplete discoverability. Free functions are less common but make sense when an operation is not closely associated with a particular type or instance.

Preferred

let sorted = items.mergeSort()  // easily discoverable
rocket.launch()  // a mutating method

Not Preferred

let sorted = mergeSort(items)
launch(&rocket)

Free Function Exceptions

let tuples = zip(a, b)  // feels natural as a free function (symmetry)
let value = max(x,y,z)  // another free function that feels natural

All 26 comments

I've also seen functions referred to as "free functions". I assume the "free" means, "not associated with any object".

Functions have more scope options than type and global, in Swift.

I just assumed normal methods in Objective-C were now referred to as functiona in Swift. All of the material I've read in terms of forums, blogs, tutorials, etc. has referred to them simply as functions, even when they belong to a class or an enum.

@mitchellporter Apple's own docs are clear about when something should be referred to as a function or a method, as per the quote above.

Tutorials on raywenderlich.com also follow Apple's guidelines here, which is why it should be rolled into the style guide.

There are both functions and methods in Objective-C too; not everything is a method. 😉

@micpringle Makes sense, but I swear I've read lots of material where they simply call it a function even when it belongs to a class. I'm guessing I'm not the only that's always using functioneither, will be interesting to see if this changes moving forward.

Are "funcs" inside methods "nested methods"?
What are funcs inside closures or other non-functions called?

struct Struct {
   let closure = {
      func whoAmI() {}
   }

   var any: Any? {
      func jeanValjean() {}
      return nil
   }
}

I think these would be classed as anonymous functions (IMO) since technically they don't "belong" to anything, and they only exist within that particular scope and can't be accessed from outside of it.

I don't think "anonymous function" is the right name here — that usually refers to closures.

I'd call them "nested functions": "functions" because they're functions, not methods attached to a named type or instance. And "nested" because...they're nested! Maybe "scoped functions" but that doesn't seem as clear.

Apologies, I didn't realise closures were also referred to as anonymous functions. :]

Nested functions sounds good to me. I agree that scoped doesn't quite sound right.

I think "nested functions" inside anything, be it a method or not, seems clear enough for the foreseeable future.

And although I do think that nested functions having names makes them "nonymous", I am unclear as to what exactly is "anonymous", however, when it comes to closures, especially stored, immutable ones, which are very much like functions. I'm leaning towards believing that the stored function is indeed anonymous, but the closure which captures it, and potentially some state, is what has the name. That's the way it's taught for C#, where lambda syntax is a shorthand for constructing what it calls a Delegate.
https://msdn.microsoft.com/en-us/library/system.delegate(v=vs.110).aspx

func nonymous() {
   func nonymous() {}
}

let anonymouses: [() -> ()] = [].map
{$0} // This "transform" is also anonymous.

let unlcearToMeWhetherNonymous = {}

It's a subtle point and possibly even a moot one once you get down to the bowels of Swift and everything is the same, but declaring something with func definitely gives the thing a name. In contrast, a closure is just some block of code with no name. You could assign it to a variable or constant, of course, but that's just storing a pointer to the thing.

Again, it's sort of an artificial difference. You could be all lisp-like and say func is just syntactic sugar for giving a name to a closure.

How about something like this:

Methods vs Free Functions

Methods are functions associated with a type and preferred because of their autocomplete discoverability. Free functions are less common but make sense when an operation is not closely associated with a particular type or instance.

Preferred

let sorted = items.mergeSort()  // easily discoverable
rocket.launch()  // a mutating method

Not Preferred

let sorted = mergeSort(items)
launch(&rocket)

Free Function Exceptions

let tuples = zip(a, b)  // feels natural as a free function (symmetry)
let value = max(x,y,z)  // another free function that feels natural

Regarding,

Free Function Exceptions

let tuples = zip(a, b)  // feels natural as a free function (symmetry)
let value = max(x,y,z)  // another free function that feels natural

"Feels natural" is pretty ambiguous and subjective.

Can we clarify what we mean here?

Also, both zip and max would also suffer from // hard to discover issue you brought up in the PR.

IMHO, I think that zip is an awkward example. Issues that I have with it:

  • What is it actually _doing_? Combining a and b? Compressing a and b somehow? Something else? Without more context, it's difficult to know.

Instead of

Free functions are less common but make sense when an operation is not closely associated with a particular type or instance.

How about this?

Free functions, which aren't attached to a class or type, should be used sparingly. When possible, prefer to use a method instead of a free function. This aids in readability and discoverability.

Free functions are most appropriate when they aren't associated with any particular type or instance.

I'm re-opening this issue to seek further discussion on Free Function Exceptions.

Well, this is a standard library function. It has precedence in language such as R, Python, C#, C++.(via boost).

Well, this is a standard library function.

Really? I haven't used it in iOS... * embarrassed *

I'll check it out... If it's widely understood/used on iOS, maybe my concern here is misplaced...

I like your verbiage suggestion... I am reluctant to give up on the zip example. (PS: The RW Swift Team has a charter that is larger (or smaller) than iOS.)

The RW Swift Team has a charter that is larger (or smaller) than iOS.

👍 True. ;]

After reviewing what does zip do, I'm for leaving it as an example. 👍

Apologies on my Swift-newbie-moment experience here. 😉

No worries. Thanks for your help as always. BTW, I just noticed you are not mentioned in the credits. I'll fix that too.

I'm not sure that free functions are ever the best solution. If they are used, I think they should add functionality that isn't addressed elsewhere.

max

let max = Swift.max(0, 1, 2)
let maxElement = [0, 1, 2].maxElement()!

maxElement should be a property, and not a method, but I still think that the free function is redundant. If these don't compile to the same thing, then I think the compiler should be improved, but even if not, I don't think the performance will ever matter; I don't think people would be using the max function with many elements.

zip
In C#, zip is implemented the equivalent of a Swift protocol extension method. I don't think that's better, but it is an example of how to handle zip differently.

In Swift, zip is the same thing as this initializer for Zip2Sequence. It would be nice to have a way to represent a zipped sequence of a variable number of input sequences, but until that happens, I think using the initializer directly is fine.

zip([1...3], ["a"..."c"])
Zip2Sequence([1...3], ["a"..."c"])

operators
The vast majority of the free functions I've written in Swift are operators. I've seen Chris Lattner and Joe Groff suggesting that operators will be able to be defined within types, à la C#, in the future, so operators may also be able to follow whatever conventions are chosen, for other functions, then. Their current implementation as free-function-only probably shouldn't be used as a guideline.

operators
The vast majority of the free functions I've written in Swift are operators. I've seen Chris Lattner and Joe Groff suggesting that operators will be able to be defined within types, à la C#, in the future...

Yep, this is likely the direction Swift will move in the future. Here's fairly recent proposal from Chris Lattner, as you mentioned.

This is especially peculiar with protocols like Equatable, which per our extension guidelines, essentially _require_ you to create an empty extension. This is awkward at best. 😞

I definitely agree that operators should not be used as an example for ideal free functions.

Thanks for the comments @Jessy- !

Some notes:

I think maxElement() is declared a method because it is not O(1) which could be surprising to some users. If you implement a property that is not O(1) it needs to be clearly documented. (In RW tutorials we have the luxury that all of the code is out in the open so we don't have to worry about that as much or enforce it as a style guideline.)

On the other hand some forms of max<T: Comparable> is O(1) and has the potential to be specialized to take advantage of hardware. (I am guessing that is why there is a two parameter version in addition to the variadic version although I haven't confirmed that.)

In any case, I think we are all in agreement that free functions should be used sparingly but I would push back against _never_. The examples used come from the standard library. I have not seen any proposals to remove the zip function from the standard library. Are there objections to the specifics of @JRG-Developer rewording or the currently merged pull request modulo this rewording?

Feel free to reopen if you think there is something further that needs to be discussed. (Merged to the update branch.)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jackwu95 picture jackwu95  ·  6Comments

icanzilb picture icanzilb  ·  6Comments

jrturton picture jrturton  ·  3Comments

designatednerd picture designatednerd  ·  22Comments

grosch picture grosch  ·  6Comments