swift-aws-lambda-runtime icon indicating copy to clipboard operation
swift-aws-lambda-runtime copied to clipboard

Lambda.run cannot be used without explicit types

Open Ro-M opened this issue 4 years ago • 5 comments

Steps to reproduce

  • setup a basic project that uses AWSLambdaRuntime
  • in your main file, define an Input and Output type that should be used for the Lambda
  • use the Lambda.run function with the non-Void callback
  • the body of the run closure MUST have at least one line of code other than invoking the callback

Observed behavior

I get a compiler error: Cannot convert value of type 'Output' to expected argument type 'Void'. There are 2 overloaded run functions: One takes a closure of type @escaping (Result<Void, Error>) -> Void and the other of type @escaping (Result<Output, Error>) -> Void. I expect the compiler to know that I am using the second function.

Example: Not compiling

import AWSLambdaRuntime

struct Input: Codable {
  let foo: String
}

struct Output: Codable {
  let bar: String
}

Lambda.run { (context, input: Input, callback) in
  context.logger.info("foo bar")
  callback(.success(Output(bar: input.foo.capitalized)))
}

Compiler Error: Cannot convert value of type 'Output' to expected argument type 'Void'

Notice that the order of those lines in the run body is not important, the following yields the same compiler error:

import AWSLambdaRuntime

struct Input: Codable {
  let foo: String
}

struct Output: Codable {
  let bar: String
}

Lambda.run { (context, input: Input, callback) in
  callback(.success(Output(bar: input.foo.capitalized)))
  context.logger.info("foo bar")
}

Example: Compiling

import AWSLambdaRuntime

struct Input: Codable {
  let foo: String
}

struct Output: Codable {
  let bar: String
}

Lambda.run { (context, input: Input, callback) in
  callback(.success(Output(bar: input.foo.capitalized)))
}

As long as the body of run does not contain more than one line I don't get any compiler error. Might be trivial but: Using lines of comments does not result in a compiler error, e.g. the following compiles as well:

import AWSLambdaRuntime

struct Input: Codable {
  let foo: String
}

struct Output: Codable {
  let bar: String
}

Lambda.run { (context, input: Input, callback) in
  // call the callback
  callback(.success(Output(bar: input.foo.capitalized)))
}

Another way to work around this problem is of course just specifying the type of callback. The following compiles without errors:

import AWSLambdaRuntime

struct Input: Codable {
  let foo: String
}

struct Output: Codable {
  let bar: String
}

Lambda.run { (context, input: Input, callback: @escaping (Result<Output, Error>) -> Void) in
  context.logger.info("foo bar")
  callback(.success(Output(bar: input.foo.capitalized)))
}


[EDIT 1: edited the code examples to make them compile if copy&pasted] [EDIT 2: reworked the examples and explanation to better reflect the problem (now more directed to the compiler error rather than the styling of the code)]

Ro-M avatar Jun 02 '20 11:06 Ro-M

cc @jckarter since most of this is compiler related

tomerd avatar Jun 02 '20 18:06 tomerd

Overloading is a bad idea to begin with in general, so if there's a way to get the intended API effect with only one overload, I would suggest that no matter what. The compiler isn't going to do type inference across statements, so you're not going to be able to get it to infer the callback argument from the body of a multi-line closure.

jckarter avatar Jun 03 '20 16:06 jckarter

+1 on not overloading, especially if there are closures involved.

weissi avatar Jun 03 '20 17:06 weissi

This will be resolved with https://github.com/apple/swift/pull/32223

fabianfett avatar Aug 10 '20 07:08 fabianfett

I believe this could be closed.

Swift 5.7 will enable such syntax, but we’re not using closures any more🤷‍♂️

stevapple avatar Jun 04 '22 09:06 stevapple