Hi,
Back Story, actual problem below
We've been trying to improve the experience for new-comers to server-side Swift and Vapor by providing a JavaScript style then
option for futures. The idea is that this would work on any EventLoopFuture
from SwiftNIO, whether the completion returned a Value
or EventLoopFuture<Value>
and whether the completion could throw or not. Essentially, this is 4 options matching map
, flatMap
, flatMapThrowing
and a flatFlatMapThrowing
, but can be condensed into 2 options - flatMapThrowing
and flatFlatMapThrowing
as they can handle completion handlers that either throw an error or don't
The Errors
I wrote two extensions for NIO's EventLoopFuture
:
import NIO
extension EventLoopFuture {
@inlinable
public func then<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Value) throws -> NewValue) -> EventLoopFuture<NewValue> {
self.flatMapThrowing(callback)
}
@inlinable
public func then<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Value) throws -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue> {
return flatMap(file: file, line: line) { result in
do {
return try callback(result)
} catch {
return self.eventLoop.makeFailedFuture(error, file: file, line: line)
}
}
}
}
These should handle all the cases we need. However the compiler is getting confused between the two. For example I have a route handler:
func testTryInMapHandler(_ req: Request) throws -> EventLoopFuture<User> {
let updatedUser = try req.content.decode(User.self)
return User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound))
.then { user in
user.name = updatedUser.name
user.username = updatedUser.username
return user.save(on: req.db).then {
try print("User ID is \(user.requireID())")
return user
}
}
}
That works if only the first function in the extension is available, but fails if both are with the error Cannot convert return expression of type 'Optional<User>.WrappedType' (aka 'User') to return type 'EventLoopFuture<User>' on return user
. I believe the compiler should be able to work this out.
This was tested with Xcode 11.4 release by the way