@functionWrapper please!

I have noticed that @_functionBuilder will be renamed @ResultBuilder in Swift 5.4, and supports applying to functions without parameters, does that mean it is possible to support any function like @propertyWrapper? It may be named @functionWrapper and allows us to intervene before and after the call of a function.

For example, @lru_cache in Python looks like what I said, it checks the parameter that has been calculated and returns the result from the cache.

Result builders aren't the same as the function wrapper feature you're describing. That feature doesn't exist in Swift, though there have been some previous discussions about adding such a feature.

Thanks for replying, I know it just treats a no parameters function as a closure. What I mean is, if the complier supports using @ResultBuilder in functions, can it also be used with other normal functions?

Yes, result builders can be applied to any function declaration, e.g.

import SwiftUI

@ViewBuilder
func test(message: String) -> some View {
  Text(message)
}

That's cool.

If you don't care about functions and you're fine with closures, you can already implement the mentioned LRU cache with property wrappers:

@propertyWrapper
class LRUCache<Parameter, Result> where Parameter: Hashable {
  let base: (Parameter) -> Result
  var cache: [Parameter: Result] = [:]
  var cacheInfo = (hits: 0, misses: 0)
  
  var wrappedValue: (Parameter) -> Result {
    return { parameter in
      if let cachedResult = self.cache[parameter] {
        self.cacheInfo.hits += 1
        return cachedResult
      } else {
        self.cacheInfo.misses += 1
        let result = self.base(parameter)
        self.cache[parameter] = result
        return result
      }
    }
  }
  
  var projectedValue: String {
    "Cache info: \(cacheInfo.hits) hits, \(cacheInfo.misses) misses, current size \(cache.count)"
  }
  
  init(wrappedValue function: @escaping (Parameter) -> Result) {
    self.base = function
  }
}

This example requires a function accepting a single parameter. With variadic generics it could be generalized. For simplicity the projected value returns the associated cache infos.

Python's lru_cache:

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

My attempt:

struct Main {
  @LRUCache static var fib: (Int) -> Int = { (n: Int) -> Int in
    if n < 2 { return n }
    return Main.fib(n - 1) + Main.fib(n - 2)
  }
}

>>> (0...15).map(Main.fib)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> Main.$fib
Cache info: 28 hits, 16 misses, current size 16

Try it!

3 Likes

Yes, we can implement @lru_cache with property wrapper to a closure, I just wonder if we can do this and also the complier can apply attribute to a function now(this is the point), it may be can create a "function wrapper" like property wrapper to support this?

The community has definitely shown interest in function wrappers for Swift - someone "just" needs to do the work to propose and implement it :slightly_smiling_face:

5 Likes

Oh, my apologies, it's not a appropriate word.

Terms of Service

Privacy Policy

Cookie Policy