RFD: Mapping issues

This is going to cause a lot of problems with functions like assert(_:_:file:line:) which take defaulted literal arguments #file and #line that only make sense in a call-site substitution model.

2 Likes

Ah, I guess that rules out re-using thunks. Although you could have the compiler default to assuming that thunks are reusable, then have the compiler flag any thunks that use #file and #line as not reusable.

I frequently find myself dealing with the problems that Erica pointed out.


About the Multiargument Map problem, there seems to be no simple solution: if the compiler generated an actual function for every possible combination of default arguments, the code size would grow exponentially, that is:

/// 1 defaulted argument => 2 functions (2^1)
func oneDefault(_ a: A = default, _ b: B) {}

/// 2 defaulted arguments => 4 functions (2^2)
func twoDefault(_ a: A = default, _ b: B = default, _ c: C) {}

/// 3 defaulted arguments => 8 functions (2^3)
func threeDefault(_ a: A = default, _ b: B = default, _ c: C = default, _ d: D) {}

/// 4 defaulted arguments => 16 functions (2^4)
func fourDefault(_ a: A = default, _ b: B = default, _ c: C = default, _ d: D = default, _ e: E) {}

/// and so on...

One possibility here would be to generate only one extra function, with all the arguments set to their default value, and keep the current behavior in every other case. This would actually cover a lot of cases that I deal with, on a daily basis.


About the unapplied method references, it is my understanding that proposal SE-0042 solves this: I didn't really follow the discussion at the time, but the proposal description clearly presents the problem of unapplied method references with no arguments (something currently resolved like (Self) -> () -> Returned) as something that the proposal solves.


I think there was already a pitch to be able to use KeyPaths as arguments of maps and other functions like that: at the time it didn't seem a problem of sufficient importance, but honestly, I use key paths all the time, and their current usage options are pointlessly limited, there is so much more that could be done with key paths. The Point-Free guys propose the introduction of a ^ prefix operator to turn a key path into a getter function (also a setter actually, depending on the context), like in the following code:

let getValueFromRoot: (Root) -> Value = ^\Root.path.to.value

This way we could keep the current .map and simply write something like:

let list: [Root] = ...
let mappedList: [Value] = list.map(^\Root.path.to.value)

This solution is clean, elegant and useful: I use something like this in production code, it works flawlessly, it's readable and easy to implement.


One additional note about key paths: Swift allows for higher-order functions that use functions as arguments, and these arguments can be both lambdas or named functions. Also, there are no fundamental differences between a function declared with func and a closure assigned to a variable. These things make Swift a very ergonomic and concise language for working with functions.

But for some reason, the "main" use case for key paths seems to be the less convenient one: using subscripts. Key paths could do some much more, and the standard library currently lacks in this area.

I actually mean that, adding just a few functions and maybe an operator to key paths really unlocks their potential, and almost raises them to the level of functional optics, with the (very important) added convenience that they provide complier generated code. I use these things all the time in production code, but I need an external dependency to unlock that potential: it would be great if we had these facilities in the standard library.