[swift-evolution] Proposal: Introduce User-defined "Dynamic Member Lookup" Types

Ben Rimmington me at benrimmington.com
Tue Nov 28 15:55:50 CST 2017


I suggest using different subscripts for function/method calls and properties:

* `value(...)`      ==> `value[dynamicFunction: ""](...)`
* `value.name(...)` ==> `value[dynamicFunction: "name"](...)`
* `value.name`      ==> `value[dynamicProperty: "name"]`

Dynamic callable types have an unnamed method.
Dynamic argument labels are always allowed (but can be ignored).
For example, the labels of `Date.UTC` below are not passed to JavaScript.

//===----------------------------------------------------------------------===//
//  JavaScriptCore.playground
//===----------------------------------------------------------------------===//

import Foundation
import JavaScriptCore

typealias Arguments = DictionaryLiteral<String, Any>

extension JSContext {

    subscript(dynamicFunction name: String) -> (_ arguments: Arguments) -> JSValue {
        return globalObject[dynamicFunction: name]
    }

    subscript(dynamicProperty name: String) -> JSValue {
        get {
            return globalObject[dynamicProperty: name]
        }
        set {
            globalObject[dynamicProperty: name] = newValue
        }
    }
}

extension JSValue {

    subscript(dynamicFunction name: String) -> (_ arguments: Arguments) -> JSValue {
        return { arguments in
            let argumentValues = arguments.map({ $0.value })
            if name.isEmpty {
                return self.call(withArguments: argumentValues)
            } else if name == "new" {
                return self.construct(withArguments: argumentValues)
            } else {
                return self.invokeMethod(name, withArguments: argumentValues)
            }
        }
    }

    subscript(dynamicProperty name: String) -> JSValue {
        get {
            return forProperty(name)
        }
        set {
            setValue(newValue, forProperty: name)
        }
    }
}

//===----------------------------------------------------------------------===//
//  Examples
//===----------------------------------------------------------------------===//

let context = JSContext()!

//: ```
//: context.isNaN(Double.nan)
//: ```
context[dynamicFunction: "isNaN"](["": Double.nan])

//: ```
//: context.Math.PI
//: ```
context[dynamicProperty: "Math"][dynamicProperty: "PI"]

//: ```
//: context.Math.PI.toFixed(5)
//: ```
context[dynamicProperty: "Math"][dynamicProperty: "PI"][dynamicFunction: "toFixed"](["": 5])

//: ```
//: context.Math.pow(2, 53)
//: ```
context[dynamicProperty: "Math"][dynamicFunction: "pow"](["": 2, "": 53])

//: ```
//: let time = context.Date.UTC(
//:     year: 9999, month: 11, day: 31,
//:     hour: 23, minute: 59, second: 59, millisecond: 999)
//: ```
let time = context[dynamicProperty: "Date"][dynamicFunction: "UTC"]([
    "year": 9999, "month": 11, "day": 31,
    "hour": 23, "minute": 59, "second": 59, "millisecond": 999])

//: ```
//: let date = context.Date.new(time)
//: ```
let date = context[dynamicProperty: "Date"][dynamicFunction: "new"](["": time])

//: ```
//: date.toISOString()
//: ```
date[dynamicFunction: "toISOString"]([:])


> On 27 Nov 2017, at 06:04, Chris Lattner wrote:
> 
> I’d like to formally propose the inclusion of user-defined dynamic member lookup types.
> 
> Here is my latest draft of the proposal:
> https://gist.github.com/lattner/b016e1cf86c43732c8d82f90e5ae5438
> https://github.com/apple/swift-evolution/pull/768
> 
> An implementation of this design is available here:
> https://github.com/apple/swift/pull/13076
> 
> The implementation is straight-forward and (IMO) non-invasive in the compiler.
> 
> -Chris



More information about the swift-evolution mailing list