To celebrate the launch of V2 of my blog, I'm starting a new series. This series will be called Nifty<Swift>. It highlights some of the quirky and nifty things you can do with the Swift language. I haven't yet determined the frequency I will be posting these, so the best way to get notified will likely be via Twitter.
First off, I found out about this feature after this reply from @TheMartianLife, so thank you Mars!
I literally only just found out bout @autoclosure and I’m obsessed with callable values as per https://t.co/JT7H0Lfbwe
— Mars Geldard 🛰 (@TheMartianLife) March 19, 2020
So, without further ado, let's jump into it:
The actual Swift proposal is here, and is titled "Callable values of user-defined nominal types". It appears to build on top of some older proposals, but I'll go over what those are in another post.
View the original swift-evolution proposal here.
What are callable values?
Well, Callable values allow you to call objects like a function. For instance (using the examples provided by the proposal), you may have a polynomial object. You could initialize this object with some coefficients, and then later on call that object like a function, plugging a value for x.
What's so great about them?
Callable values are great syntactically, as it makes it much nicer to use than having a function (syntactically) that you need to call on the object.
For instance, it's much nicer to write MyPolynomial(3)
than it is to write MyPolynomial.evaluated(at: 3)
How can I use them?
Glad you asked (did you, really?). To use callable values, you create a method with the identifier callAsFunction
. That's it! Super simple.
Check out the below example, where I write a distance calculator (useful for calculating distance travelled when drivers are distracted and going certain speeds).
In the example below, I make use of Swift Measurement
objects. The user specify's speed using a UnitSpeed
object, and the distance travelled is then returned via the callAsFunction
method.
In the below example, 'metres
' has the value 27.7778, which is 27.7778 metres per second, and is returned as a Measurement<UnitLength>
.
// James Dale
import Foundation
struct DistanceCalculator {
let speed: Measurement<UnitSpeed>
}
extension DistanceCalculator {
/// Callable
/// - Parameter time: The time in seconds
/// - Returns: The distance travelled, in metres
func callAsFunction(_ time: TimeInterval) -> Measurement<UnitLength> {
let metresPerSecond = speed.converted(to: .metersPerSecond)
return Measurement<UnitLength>(value: metresPerSecond.value * time, unit: .meters)
}
}
let car = DistanceCalculator(speed: .init(value: 50, unit: .kilometersPerHour))
let metres = car(2)
