Pitch: Introduce (static) callables

Update: the callable WIP branch is available at Introduce callables. by dan-zheng · Pull Request #23517 · apple/swift · GitHub.
The major change is the addition of the new call declaration kind, as described in this pitch.

Parsing is nearly done, sans an unideal parsing diagnostic to fix.

  • call is a keyword only in declaration-parsing contexts so func call(...) and call(...) apply expression are still valid.

  • Added parsing source compatibility tests, here's an excerpt:

    // Test context sensitive parsing.
    // "call" can appear in declaration or expression contexts.
    struct ContextSensitiveParsing {
      // declaration
      call(_ fn: () -> Void) {
        // expression
        call() {}
        // expression
        call {}
    
        struct U {
          // declaration
          call(x: Int) {}
    
          // error
          // expected-error @+1 {{expected '(' for 'call' member parameters}} {{11-11=()}}
          call {}
        }
      }
    }
    
    // Global function. Used below.
    func call(_ fn: () -> Void) {}
    
    // expression
    call() {}
    

Type-checking needs a bit more work needs a bit more work, namely "looking up all call declarations" in a robust way. This means call declarations don't quite work as protocol requirements yet, but this will be fixed soon.

These examples work though:

struct SimpleCallable {
  call(_ x: Float) -> Float {
    return x
  }
}

let foo = SimpleCallable()
_ = foo(1)
_ = foo(foo(1))

// Test direct references.
_ = foo.call(1)
_ = [1, 2, 3].map(foo.call)
_ = foo.call(foo(1))
_ = foo(foo.call(1))
let _: (Float) -> Float = foo.call

// Generics work too.
struct Generic {
  call<T, U>(_ x: T, _ y: U) -> (T, U) {
    return (x, y)
  }
}
let generic = Generic()
_ = generic(1, 3.0)
_ = generic(generic, generic.call as ([Int], Float) -> ([Int], Float))

Feel free to take a look if you're interested - feedback appreciated!

8 Likes