programming, user experience, and more


Practical Use for Curried Functions in Swift

When I first discovered curried functions in Swift, I though it was very cool, especially because methods are implemented as curried functions, but I couldn't think of a practical use case for them. If you don't already know what a curried function is, it is basically a function that is made up of a chain of functions. This will become more clear through my practical example.

Curried functions work great for implementing a logging system. Here is my example Logger type:

struct Logger {
    /// Level of log message to aid in the filtering of logs
    enum Level: Int, Printable {
        /// Messages intended only for debug mode
        case Debug = 3

        /// Messages intended to warn of potential errors
        case Warn =  2

        /// Critical error messagees
        case Error = 1

        /// Log level to turn off all logging
        case None = 0

        var description: String {
            switch(self) {
                case .Debug:
                    return "Debug"
                case .Warn:
                    return "Warning"
                case .Error:
                    return "Error"
                case .None:
                    return ""
            }
        }
    }

    /// Log a message to the console
    ///
    /// :param: level What level this log message is for
    /// :param: name A name to group a set of logs by
    /// :param: message The message to log
    ///
    /// :returns: the logged message
    static func log
        (#level: Level)
        (name: @autoclosure() -> String)
        (message: @autoclosure() -> String) -> String
    {
        if level.toRaw() <= Logger.logLevel.toRaw() {
            let full = "\(level.description): \(name()) - \(message())"
            println(full)
            return full
        }
        return ""
    }

    /// What is the max level to be logged
    ///
    /// Any logs under the given log level will be ignored
    static var logLevel: Level = .Warn

    /// Logger for debug messages
    static var debug = Logger.log(level: .Debug)

    /// Logger for warnings
    static var warn = Logger.log(level: .Warn)

    /// Logger for errors
    static var error = Logger.log(level: .Error)
}

My logger only had to implement a single log method that takes a level, name, and message. A full log call would look like this:

Logger.log(level: .Debug)(name: "SomeName")(message: "message")
// Prints "Debug: SomeName - message

However, because I defined log as a curried function, I was able to also define specially named partials for each of the different log levels. This allows a debug log call to look like this:

Logger.debug(name: "SomeName")(message: "message 2")
// Prints "Debug: SomeName - message 2"

Finally, I can also define my own partials if I am going to make a series of logs all with the same name:

let myLog = Logger.debug(name: "My")
myLog(message: "message 3")
// Prints "Debug: My - message 3"

Curried functions allow me to have a logger that has a level, name, and message without forcing me to specify them all each time I want to log something.

There are definitely other ways to offer similar functionality without curried functions, but this is a very succinct and flexible way to implement it.