With the acceptance and release of SE-399 and SE-408, using pack expansion has become more appealing and more common in code. At the same time, there are some sharp edges that would be useful to round out.
The common limitation in many actions is the ability to perform a transformation on a value pack. I propose support for a syntax that allows blocks to be passed in value packs so that functions such as map can exist. For example:
func map<each TPack, each TOutput>(
pack: (repeat each TPack),
transform: repeat (each TPack) -> each TOutput
) -> (repeat each TOutput) {
(repeat (each transform)(each pack))
}
There's no way to throw more than one error type (so I don't think this overload is very useful, even though one that applied the same transformation across a heterogeneous tuple would be), but you should at least support that, too.
@_documentation(visibility: private) // DocC can't cope with this signature.
@inlinable public func map<each Input, each Output, Error>(
_ input: (repeat each Input),
transforms transform: repeat (each Input) throws(Error) -> each Output
) throws(Error) -> (repeat each Output) {
(repeat try (each transform)(each input))
}
It would would be great to support a specific error type. The code below seems to work, for transforms that all are non-throwing, throwing (i.e., any Error), and throwing a specific error type (throws(SomeErr)).
public static func map<each Input, each Output, E: Error>(
_ input: (repeat each Input),
transform: repeat (each Input) throws(E) -> each Output
) throws(E) -> (repeat each Output) {
(repeat try (each transform)(each input))
}
However, when throwing a specific type, each transform seems to require the same throws(E). Normally a function with throws(Never) should be convertible/assignable to one as throws(E), so I was hoping that some transforms/closures could be non-throwing (i.e., throws(Never)) and others specific throws(E), and the map(..) would end up as throws(E).
Instead (in 6.1) the function type of a repeat pack with some non-throwing closures and a specific throws(E) seems to resolve instead as throws(any Error), which in turn devolves the map function type and requires catch-all handlers in any clients.
Is this a bug? Or is it an unavoidable limitation because the transform/function types all have different parameter and result types?
It doesn't have anything to do with that. There's just no syntax for the compiler to generate typed throwing overloads for the mixed cases in question. The closest there is, is rethrows, but even though this syntax compiles, with typed errors only for parameters, I think it's pointless?
The options I can think of that you get with closures and typed errors are:
Use a named closure.
Explicitly type the closure, inside itself. You can use the placeholder underscore, so it's not that bad. But you lose the ability to use $0, which is that bad.
Explicitly type the closure, outside itself. You can use placeholder underscores, but in the exact opposite places of the previous option. I.e. the error type can't be inferred.
Add typing via another function. This requires escaping, so I don't use it.
func f() throws(E) {
_ = try map(
(0, "1", true),
transforms:
E.throw, // 1
{ x throws(_) in x }, // 2
\.self as (_) throws(E) -> _, // 3
addTypedError(\.self) // 4
)
}
struct E: Error {
static func `throw`<T>(_: T) throws(Self) -> T { throw .init() }
}
func addTypedError<each Parameter, Return, Error>(
_ closure: @escaping (repeat each Parameter) -> Return
) -> (repeat each Parameter) throws(Error) -> Return {
closure
}
@_documentation(visibility: private) // DocC can't cope with this signature.
@inlinable public func map<each Input, each Output, Error>(
_ input: (repeat each Input),
transforms transform: repeat (each Input) throws(Error) -> each Output
) throws(Error) -> (repeat each Output) {
(repeat try (each transform)(each input))
}
However, because of what I said above, where parameter packs don't do anything to ameliorate raising Never + some other concrete Error to any Error, I don't think any of this is that relevant to this thread.