[Proposal/Pitch] Function decorators


(Aleksandar Petrovic) #1

Hi swift-evolution,

I want to apologize in advance for my clumsy English. It's (obviously) not my first language.

Recent discussion about property behaviours reminded me of function decorators in Python. I think decorators can nicely fit in Swift, too.

First, a bit of explanation for the uninitiated. Decorator is a function that transform other function - it receives some function and returns a function of the same signature. Lets make some dead simple decorator:

typealias Decorated = (Double, Double) -> Double

func hiDecorator(fn: Decorated) -> Decorated {
return { x, y in
print("Hi from decorator!")
return fn(x, y)
}
}

func multiply(a: Double, _ b: Double) -> Double {
return a * b
}

let decoratedMultiply = hiDecorator(multiply)
print("Result: \(decoratedMultiply(2, 3))")

The above code should print:
Hi from decorator!
Result: 6

We can use decorators with the current Swift, but they're a bit cumbersome - either we need to store decorated function and remember to use it instead of the original one, or remember to do the decoration on every call.

Instead, we can write something like this:

[hiDecorator]
func multiply(a: Double, _ b: Double) -> Double {
return a * b
}

... and that code should be transformed into following during compilation:

func multiply(a: Double, _ b: Double) -> Double {
let fn: (Double, Double) -> Double { _a, _b in
return _a * _b
}

 let decorated = hiDecorator\(fn\)     
 return decorated\(a, b\)

}

Outside, the function looks like before, so the change is compatible with virtual or interface extension functions.

Sometimes we'll need to pass some additional data to the decorator. That's fine, as long as the last argument is the target function:

func logUsage<I, O>(fnName: String, fn: (I) -> O) -> ((I) -> O) {
return { _i in
print("Function \(fnName) enter")
defer { print("Function \(fnName) exit") }
return fn(_i)
}
}

Let's use it:

[logUsage("increment")]
func increment(a: Int) -> Int { return a + 1 }

[logUsage("justPrint")]
func justPrint(s: String) { print(s) }

In the above code, the compiler should generate internal decorator call by combining provided parameter and decorated function.

Why decorators?

It's important to say that proposed decorators change are pure syntactic sugar. Everything we can do with this change can be accomplished with the current Swift language. But, it gives to programmer a tool to express intentions more clearly and move repeating code away.

Sometimes decorators are just a convenient way to quickly add code. In the example above, logging is added with just one line of code and can be easily removed - the function body is not changed.

Sometimes decorators can be used as a poor man dependency injection mechanism, so we can write functions like this:

// if conn is nil, dbInit decorator will provide (global) one, and properly close it after doSomeDbWork exits
[dbInit]
func doSomeDbWork(conn: DbConnection? = nil) { ... }

We can use decorators in the standard library to, in a way, extend language:

[asyncMain]
func doSomething() { ... } // this function will be posted to main queue

[synchronized]
func foo() { ... } // synchronized decorator uses either objc_sync_enter/exit or post on some queue to ensure mutual exclusion

Or to do some more exotic operations:

[repeatUntilDone(maxAttempt: 5)]
func connectToServer(url: String) -> Bool { ... }

Abstract functions are frowned upon in Swift community, but if we can't live without it, we can signal our intent more clearly with decorator:

class T {
[abstract] func bar() {} // abstract decorator will assert on call
}

Etc, etc. Sounds interesting?

Alex


(Karl) #2

Given that we might be doing "property behaviours‚ÄĚ (https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md), which are basically property decorators, it probably makes sense to look at how that could work for function decorators.

After all, you could create a property behaviour around a closure and use that as a kind of function decorator. It would just be messy.

Karl

···

On 4 May 2016, at 00:46, Aleksandar Petrovic via swift-evolution <swift-evolution@swift.org> wrote:

Hi swift-evolution,

I want to apologize in advance for my clumsy English. It's (obviously) not my first language.

Recent discussion about property behaviours reminded me of function decorators in Python. I think decorators can nicely fit in Swift, too.

First, a bit of explanation for the uninitiated. Decorator is a function that transform other function - it receives some function and returns a function of the same signature. Lets make some dead simple decorator:

typealias Decorated = (Double, Double) -> Double

func hiDecorator(fn: Decorated) -> Decorated {
    return { x, y in
        print("Hi from decorator!")
        return fn(x, y)
    }
}

func multiply(a: Double, _ b: Double) -> Double {
   return a * b
}

let decoratedMultiply = hiDecorator(multiply)
print("Result: \(decoratedMultiply(2, 3))")

The above code should print:
Hi from decorator!
Result: 6

We can use decorators with the current Swift, but they're a bit cumbersome - either we need to store decorated function and remember to use it instead of the original one, or remember to do the decoration on every call.

Instead, we can write something like this:

[hiDecorator]
func multiply(a: Double, _ b: Double) -> Double {
    return a * b
}

... and that code should be transformed into following during compilation:

func multiply(a: Double, _ b: Double) -> Double {
     let fn: (Double, Double) -> Double { _a, _b in
         return _a * _b
     }
     
     let decorated = hiDecorator(fn)
     return decorated(a, b)
}

Outside, the function looks like before, so the change is compatible with virtual or interface extension functions.

Sometimes we'll need to pass some additional data to the decorator. That's fine, as long as the last argument is the target function:

func logUsage<I, O>(fnName: String, fn: (I) -> O) -> ((I) -> O) {
    return { _i in
        print("Function \(fnName) enter")
        defer { print("Function \(fnName) exit") }
        return fn(_i)
    }
}

Let's use it:

[logUsage("increment")]
func increment(a: Int) -> Int { return a + 1 }

[logUsage("justPrint")]
func justPrint(s: String) { print(s) }

In the above code, the compiler should generate internal decorator call by combining provided parameter and decorated function.

Why decorators?

It's important to say that proposed decorators change are pure syntactic sugar. Everything we can do with this change can be accomplished with the current Swift language. But, it gives to programmer a tool to express intentions more clearly and move repeating code away.

Sometimes decorators are just a convenient way to quickly add code. In the example above, logging is added with just one line of code and can be easily removed - the function body is not changed.

Sometimes decorators can be used as a poor man dependency injection mechanism, so we can write functions like this:

// if conn is nil, dbInit decorator will provide (global) one, and properly close it after doSomeDbWork exits
[dbInit]
func doSomeDbWork(conn: DbConnection? = nil) { ... }

We can use decorators in the standard library to, in a way, extend language:

[asyncMain]
func doSomething() { ... } // this function will be posted to main queue

[synchronized]
func foo() { ... } // synchronized decorator uses either objc_sync_enter/exit or post on some queue to ensure mutual exclusion

Or to do some more exotic operations:

[repeatUntilDone(maxAttempt: 5)]
func connectToServer(url: String) -> Bool { ... }

Abstract functions are frowned upon in Swift community, but if we can't live without it, we can signal our intent more clearly with decorator:

class T {
     [abstract] func bar() {} // abstract decorator will assert on call
}

Etc, etc. Sounds interesting?

Alex
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Erica Sadun) #3

In Objective-C, you can add a category that will entirely replace an existing method. Could using decorators allow us to add behaviors to methods to extend their behavior without overwriting those methods? Here is an example of what I'm thinking.

extension UIView {
   decorate func somethingElse(...) {
         // performs existing behavior of somethingElse
        ... adds new behavior ...
   }
}

Context: I was asking about whether this kind of "extend rather than replace or subclass" behavior were possible in Swift, and was told that "decorators" might be the right technology to implement it.

Thanks in advance for any insight.

-- E

···

On May 8, 2016, at 10:33 AM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 4 May 2016, at 00:46, Aleksandar Petrovic via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi swift-evolution,

I want to apologize in advance for my clumsy English. It's (obviously) not my first language.

Recent discussion about property behaviours reminded me of function decorators in Python. I think decorators can nicely fit in Swift, too.

First, a bit of explanation for the uninitiated. Decorator is a function that transform other function - it receives some function and returns a function of the same signature. Lets make some dead simple decorator:


(Aleksandar Petrovic) #4

Well, yes and no. A decorator receives a method and returns a new method, and it's up to implementation what will happen in the new method - it can perform some operations before calling the original method, or doing something after the original method is called, or not calling the original method at all. But a decorator must be applied to the function in compile time, so changing the behavior of functions from some library we don't have source (like UIView, in your example) wouldn't be possible.

At least according to my original proposal. The proposal can be changed, but we need to invent some new syntax for such decorators.

Alex

···

On May 16, 2016 at 16:25:08, Erica Sadun (erica@ericasadun.com) wrote:

On May 8, 2016, at 10:33 AM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 4 May 2016, at 00:46, Aleksandar Petrovic via swift-evolution <swift-evolution@swift.org> wrote:

Hi swift-evolution,

I want to apologize in advance for my clumsy English. It's (obviously) not my first language.

Recent discussion about property behaviours reminded me of function decorators in Python. I think decorators can nicely fit in Swift, too.

First, a bit of explanation for the uninitiated. Decorator is a function that transform other function - it receives some function and returns a function of the same signature. Lets make some dead simple decorator:

In Objective-C, you can add a category that will entirely replace an existing method. Could using decorators allow us to add behaviors to methods to extend their behavior without overwriting those methods? Here is an example of what I'm thinking.

extension UIView {
decorate func somethingElse(...) {
// performs existing behavior of somethingElse
... adds new behavior ...
}
}

Context: I was asking about whether this kind of "extend rather than replace or subclass" behavior were possible in Swift, and was told that "decorators" might be the right technology to implement it.

Thanks in advance for any insight.

-- E


(Karl) #5

I was thinking more along the lines of property behaviours, which are specified at compile-time. Decorating an existing function inside an extension would only be possible for overridable functions in non-final types (otherwise the functions may have been inlined). But yeah, theoretically I think you could capture the existing function pointer and replace it in the vtable with a decorated one. That is basically what you’d do in Objective-C today (getting the IMP for a selector, making an IMP from a closure and switch them in the dispatch table, then call original IMP from your closure).

···

On 16 May 2016, at 16:25, Erica Sadun <erica@ericasadun.com> wrote:

On May 8, 2016, at 10:33 AM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 4 May 2016, at 00:46, Aleksandar Petrovic via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi swift-evolution,

I want to apologize in advance for my clumsy English. It's (obviously) not my first language.

Recent discussion about property behaviours reminded me of function decorators in Python. I think decorators can nicely fit in Swift, too.

First, a bit of explanation for the uninitiated. Decorator is a function that transform other function - it receives some function and returns a function of the same signature. Lets make some dead simple decorator:

In Objective-C, you can add a category that will entirely replace an existing method. Could using decorators allow us to add behaviors to methods to extend their behavior without overwriting those methods? Here is an example of what I'm thinking.

extension UIView {
   decorate func somethingElse(...) {
         // performs existing behavior of somethingElse
        ... adds new behavior ...
   }
}

Context: I was asking about whether this kind of "extend rather than replace or subclass" behavior were possible in Swift, and was told that "decorators" might be the right technology to implement it.

Thanks in advance for any insight.

-- E