32

Say I have have a class Event as follows:

class Event {
    private var attendees: [Person] = []

    // Case 1
    //*******
    // Should I use a func…
    func countOfAttendees() -> Int {
        return attendees.count
    }

    // …or a var
    var countOfAttendees: Int {
        return attendees.count
    }

    // Case 2
    //*******
    // Should I use a func…
    func countOfPaidAttendees() -> Int {
        return attendees.filter({$0.hasPaid}).count
    }

    // …or a var
    var countOfPaidAttendees: Int {
        return attendees.filter({$0.hasPaid}).count
    }
}

Is it best practice to use functions or computed properties in the 2 cases indicated above?

6 Answers6

19

Follow the Uniform Access Principle,

All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation

To me, this means that I don't write funcs that take no arguments and return a value. I always use computed properties. That way, if I later decide to change the computed property into a stored property, I can do so without having the urge to remove the parens everywhere in my app and without having a separate "getter" method that just returns the value of a stored property, which seems pretty wasteful IMHO.

And if I change a stored property into a computed one, I don't have to add parens to the end of it, and everywhere that it is used in the app.

Daniel T.
  • 3,053
18

I would say it depends on complexity of calculation vs. usage frequency.

  • If it's O(1) / *, then use computed property.
  • If it's O(N)+ / rare-use, then use function.
  • If it's O(N)+ / frequent-use, think whether in the future you might decide to use caching or other "smart" techniques to compensate for the complexity, if "yes" then use property, if "no-no-no, it's just heavy" then use function.
12

I recently started learning Kotlin and they have a great heuristic about when to use computed properties:

Functions vs Properties

In some cases functions with no arguments might be interchangeable with read-only properties. Although the semantics are similar, there are some stylistic conventions on when to prefer one to another.

Prefer a property over a function when the underlying algorithm:

  • does not throw
  • has a O(1) complexity
  • is cheap to calculate (or caсhed on the first run)
  • returns the same result over invocations

-- https://kotlinlang.org/docs/reference/coding-conventions.html

Daniel T.
  • 3,053
9

Use countOfAttendees and countOfPaidAttendees().


A computed variable is one that returns a calculated value each time it is accessed. That is, it doesn’t store a value. Internally it is implemented as a function.

What is the difference with a function?

  • Semantically, a variable is state, a function is an action.
  • A function regulates access to private storage. A calculated variable may do the same in a more compact way. Example.
  • A computed variable can be used with KVO, passed as a #keypath, and has facilities for observing: willSet, didSet.

You should use a variable when

  • it does not throw
  • it returns a simple property
  • it doesn’t have a side effect or a verb in its name
  • it’s O(1), that is, it doesn’t incur a significant cost. In your example it will be O(n).
  • it is idempotent. Multiple identical invocations return the same value or set the object to the same state.

Irrelevant reasons to prefer a variable over a function

  • A computed variable saves you from typing (). However, clarity is more important than brevity, so this is a weak argument.
  • A read only variable can be overriden as read/write. A function indicates it is always read only. However, Apple uses properties for read-only variables like array.count. When in doubt seek consistency with the platform.

Resources

From WWDC 2014 - 204 What’s new in Cocoa > 24:40 When to use a @property

Use property for anything that is about the value or state of an object or its relationship to other objects. Bad candidates:

  • Methods that do things: load, parse, toggle, …. They have verbs in its name.
  • Generators: init, copy, enumerated, …. These methods are not idempotent.
  • Methods which change state: nextObject.

From Swift Style by Erica Sadun > Computed Properties vs. Methods

A property expresses an inherent quality of an instance, while a method performs an action.

  • Methods have parameters; properties don’t. Prefer methods for any call with side effects. If a method does something (for example, it loads, parses, toggles, or prints) or has a verb name, it should not be a property.
  • Prefer properties for simple values that you can get and/or set.
  • Properties should express a semantic intrinsic quality of a type instance.
  • Properties allow you to add observers via willSet and didSet. Unlike stored instance properties, stored type properties must always be given a default value.

From Kotlin coding conventions > functions vs properties. See Daniel’s answer above.

Other resources with no relevant information:

Jano
  • 267
8

In Swift, functions without parameters and computed properties have almost the same capabilities (there may be a difference that a function without parameter is also a closure, while a computed property isn't).

The difference is semantically. If your code performs an action and returns for example a description of the outcome of that action, then I would use a function. If your code calculates a property but from the user's point of view this could have been a stored property, or maybe a stored property that requires updating some cached value first, then I would use a calculated property.

A big difference: What happens if you call the function or the calculated property twice? For a calculated property I expect that x = property; y = property has exactly the same behaviour as x = property; y = x except it might run a tiny bit slower. For functions, I wouldn't be surprised if the behaviour was different.

gnasher729
  • 49,096
3

I'd use a func. Object-oriented programming works just fine without computed properties. Because you are getting a value back that was computed/filtered some may argue that a computed property feels right. But here's my complaint, if you do that then readability takes a hit, because it feels like a value.

In this context it wouldn't make sense to try to assign the computed value (and luckily the IDE helps us avoid this) but what if I try to assign something that is computed but looks like a value?

event.countOfAttendees = 0; // not possible

While using func the caller know's you aren't dealing with a value directly:

event.countOfAttendees()

I think if its a behavioral object it should look like it behaves rather than looking like a data structure. If your object is dumb and doesn't have any behavior then why try to encapsulate it? In that case you might as well just have attendees be public