@davedelong's review of SE-0289 Function Builders got me thinking about all the other "syntactic transforms that turn into normal function calls / member accesses":
@main
callAsFunction
-
subscript(dynamicMember:)
(the key path one) - string interpolation
@dynamicCallable
-
subscript(dynamicMember:)
(the string one) - expression-based case matching (
~=
)
Even without designing a full macro system, even if they're still hardcoded in the compiler, it feels like it would be nice in several ways to have a way to describe these entry points in the language itself:
- Reusable logic for code completion, when implementing such a type
- Reusable logic in the compiler for making the transformations
- A place to put doc comments
If, for now, we set aside ambitions of anything but doc comments, what might this look like?
syntactic protocol MainEntryPoint {
static func main()
}
syntactic protocol Callable {
// '*' for "any arguments" to avoid confusion with existing variadics
// when there are other required arguments.
func callAsFunction(*)
}
syntactic protocol CaseMatching {
// A little funny since it does a normal operator lookup here,
// not just into the type.
static func ~= <Input>(_ expected: Self, _ actual: Input) -> Bool
}
syntactic protocol DynamicMemberLookup {
subscript<Name: ExpressibleByStringLiteral, Value>(
dynamicMember member: Name
) -> Value { get set }
subscript<Key: AnyKeyPath, Value>(
dynamicMember keyPath: Key
) -> Value { get set }
}
syntactic protocol DynamicCallable {
// 'optional' in that the compiler will use something else
// if the client doesn't implement this, instead of just an error
optional func dynamicallyCall<Arguments: ExpressibleByArrayLiteral, Result>(
withArguments: Arguments
) -> Result
func dynamicallyCall<Arguments: ExpressibleByDictionaryLiteral, Result> (
withKeywordArguments: Arguments
) -> Result where Arguments.Key: ExpressibleByStringLiteral
}
syntactic protocol StringInterpolation where Self: StringInterpolationProtocol {
func appendInterpolation(*)
}
And the big one:
syntactic protocol FunctionBuilder {
optional func buildExpression<Input, Output>(_ expression: Input) -> Output
func buildBlock<Output>(*) -> Output
optional func buildFinalResult<Input, Result>(_ component: Input) -> Result
func buildOptional<Input, Output>(_ input: Input?) -> Output
optional func buildEither<Input, Output>(first: Input) -> Output
optional func buildEither<Input, Output>(second: Input) -> Output
func buildArray<Input, Output>(_ components: [Input]) -> Output
optional func buildLimitedAvailability<Input, Output>(_ component: Input) -> Output
}
There's a lot to explore here, so, thoughts?