I had a syntax idea that I'd like to hear thoughts on - I'm not currently leaning towards it as a genuinely good idea because I know enough to know that I'm not seeing all of the potentially problematic consequences. It is intriguing however and perhaps with some other minds at the table it could be brought to a fruitful state. The idea is essentially to be able to define reusable function parameter lists in the form of structs.
Here's a simple struct:
struct Foo {
let bar: Bool
let baz: Double
let bop: String
}
Here's a function that requires a Foo
value:
func doSomething (_ foo: Foo) {
}
I can currently call it like this:
doSomething(
Foo(
bar: true,
baz: 10.1
bop: "hello"
)
)
What I'd like is to be able to call it like this:
doSomething(
bar: true,
baz: 10.1,
bop: "hello"
)
The rule I have in mind is that in the case where a function takes only one argument then that function automatically acquires an overload for each initializer of the argument type. In my example I have no external argument label for foo
in the parent function, but I suppose that any external argument label should be ignored in the case that the parameter list of a Foo.init
is being used.
In theory this should apply recursively. Here's the example reasoning that I'm imagining the compiler would do:
- When resolving a function call, obviously check first for an explicitly defined overload that matches the provided arguments and argument labels.
- If no match is found find all overloads which accept only one argument.
- For each unique type from these only-child arguments, find all accessible initializers for the argument.
- Synthesize an overload for the parent function using the parameter list from each of these initializers from step 3.
- Attempt to resolve the function call in the context of all these newly synthesized overloads.
- During this process, we may have synthesized new overloads which themselves only take one argument. If after the first round we have still not been able to resolve the call to the parent function successfully then we can repeat the process on these newly generated one-argument initializers.
To clarify the possibility of the recursive aspect:
struct Foo {
let bar: Bool
let baz: Double
let bop: String
}
struct Bar {
let foo: Foo
}
func doSomethingElse (_ bar: Bar) {
}
func demo () {
/// This overload of `doSomethingElse` is recursively generated
/// by finding firstly that a `Bar` can be initialized with only a `Foo`
/// and secondly that a `Foo` can be initialized with `bar:baz:bop:`
doSomethingElse(
bar: true,
baz: 10.2,
bop: "hello"
)
}