Skip to content

Instantly share code, notes, and snippets.

@ignazioc
Created February 12, 2017 10:39
Show Gist options
  • Save ignazioc/df0a7ea65f5cafc3715c83f5b6829046 to your computer and use it in GitHub Desktop.
Save ignazioc/df0a7ea65f5cafc3715c83f5b6829046 to your computer and use it in GitHub Desktop.
A simple explanation of the @autoclosure keyword
/*:
### Swift 3 @autoclosure
*/
import Cocoa
// Let's suppose this is complex funcion with lookup database etc.
func nextOrderNumber() -> Int {
return 1000
}
//: Let's create 3 different functions to build an order.
// This first funcion takes an Int as order number, so far soo god.
// The type is (String, Int) -> String
func buildOrder(customerName: String, orderNumber: Int) -> String {
if (customerName == "David Copperfield") {
return "Sorry we cannot create and order for this guy"
}
return "Created order n: \(orderNumber) for \(customerName)"
}
//This is how the first funcion should be called
//Pros: The function is straight forward.
//Cons: The expensive function `nextOrderNumber` is called even if the customer is "David Copperfield", we could save some resources there.
buildOrder(customerName: "David Copperfield", orderNumber: nextOrderNumber())
// The second version of the builder takes a clousure
// The type is (String, (() -> Int) ) -> String
func buildOrder(customerName: String, orderNumberFetcher: ()->Int ) -> String {
if (customerName == "David Copperfield") {
return "Sorry we cannot create and order for this guy"
}
return "Created order n: \(orderNumberFetcher()) for \(customerName)"
}
//This is how the second function should be called
//Pros: The expensive function `nextOrderNumber` is not called until it's really needed.
//Cons: Nothing really, but a clousure parameter makes the funcion hader to understand.
buildOrder(customerName: "David Copperfield", orderNumberFetcher: nextOrderNumber)
// The third version of the builder uses an @autoclousure
// The function type is (String, @autoclosure () -> Int) -> String but it should be used as a `(String, Int) -> String` function.
func buildOrder(customerName: String, orderNumberFetcher: @autoclosure () -> Int ) -> String {
if (customerName == "David Copperfield") {
return "Sorry we cannot create and order for this guy"
}
return "Created order n: \(orderNumberFetcher()) for \(customerName)"
}
//This is how the third function should be called.
//Notice: An `int` is required as second parameter but, because of the @autoclousure keyword an eventual function is not called until needed.
// The way of the function is called is exactly the same as the first function, but the `nextOderNumber` is not called if not required
//Pros: The expensive function `nextOrderNumber` is not called until it's really needed.
//Cons: It can be hard to understand that `nextOrderNumber()` execution is postponed.
buildOrder(customerName: "David Copperfield", orderNumberFetcher: nextOrderNumber())
/*:
### A more real case
*/
//The @autoclousure can be usefull for logging funcions like in this example.
enum LogLevel : Int, Comparable {
case None
case Error
case Debug
case Verbose
public static func < (a: LogLevel, b: LogLevel) -> Bool {
return a.rawValue < b.rawValue
}
}
let globalLogLevel = LogLevel.Error
//This function takes an @autoclosure string as parameter
func log(logLevel: LogLevel, message: @autoclosure () -> String) {
if (logLevel <= globalLogLevel) {
//Call the clousure only if needed
print("DEBUG: \(message())")
}
}
//With this approach, we can log `anExpensiveFunction` calling the function olny when it' really needed.
func anExpensiveFunction() -> String { return "hello world" }
log(logLevel: LogLevel.Verbose, message: anExpensiveFunction())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment