Skip to content

Conversation

@xiii111
Copy link
Contributor

@xiii111 xiii111 commented Dec 10, 2023

Resolve #155

Cause

struct Foo {
  @DependencyEndpoint
  var bar: (_ a: Int) -> Void
}

In general, the above code can typically be transformed into the following code without any issue. This is because the type of argument a passed to func bar matches the type of the argument a passed to var bar.

func bar(a p0: Int) -> Void {
  self.bar(p0)
}

This also applies to simple closures.

struct Foo {
  @DependencyEndpoint
  var bar: (_ a: () -> Int) -> Void
}

func bar(a p0: () -> Int) -> Void {
  self.bar(p0)
}

However, when considering closures marked with @autoclosure, the type of the argument a passed to func bar might not match the type of the argument a when passing to var bar.

struct Foo {
  @DependencyEndpoint
  var bar: (_ a: () @autoclosure -> Int) -> Void
}

func bar(a p0: @autoclosure () -> Int) -> Void {
  self.bar(p0) // Error occurs: 'Add () to forward @autoclosure parameter'
}

Solution

Simply adding () should resolve this bug.

struct Foo {
  @DependencyEndpoint
  var bar: (_ a: () @autoclosure -> Int) -> Void
}

func bar(a p0: @autoclosure () -> Int) -> Void {
  self.bar(p0()) // No Error occurs
}

Concern

I might be overly concerned, but I feel there's a slight difference between directly invoking var bar and indirectly through func bar. When directly calling var bar, it receives () -> String. However, when calling var bar indirectly through func bar, it receives () -> (() -> String)(). I don't expect this difference to cause any change in behavior, but if there might be an issue, I'd appreciate some advice!

Comment on lines +167 to +171
$1.isInout
? "&p\($0)"
: $1.isAutoclosure
? "p\($0)()"
: "p\($0)"
Copy link
Contributor Author

@xiii111 xiii111 Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#159 style seemed so cool that I had to try it myself. Originally, I was writing code like this 😅

let appliedParameters =
  parameters
  .enumerated()
  .map {
    if let typed = $1.type.as(AttributedTypeSyntax.self),
       typed.specifier?.tokenKind == .keyword(.inout) {
      return "&p\($0)"
    }
    
    if let typed = $1.type.as(AttributedTypeSyntax.self),
       typed.attributes.contains(where: {
         $0.as(AttributeSyntax.self)?.attributeName.as(IdentifierTypeSyntax.self)?.name.tokenKind == .identifier("autoclosure")
       }) {
      return "p\($0)()"
    }

    return "p\($0)"
  }
  .joined(separator: ", ")

.attributeName
.as(IdentifierTypeSyntax.self)?
.name
.tokenKind == .identifier("autoclosure")
Copy link
Contributor Author

@xiii111 xiii111 Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#159 uses text but I thought tokenKind might be a bit better. Since I'm new to SwiftSyntax, I'll respect your choice.

}

extension TupleTypeElementSyntax {
fileprivate var isAutoclosure: Bool {
Copy link
Contributor Author

@xiii111 xiii111 Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To obtain this code, I used the Swift AST Explorer. When I input

var bar: (_ a: @autoclosure () -> Int) -> Void

it produced the following output.

スクリーンショット 2023-12-10 19 38 27

"""
struct Foo {
@DependencyEndpoint
var bar: (_ a: @autoclosure () -> Int, _ b: () -> Int, _ c: @autoclosure () -> Int) -> Void
Copy link
Contributor Author

@xiii111 xiii111 Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the same naming with #159, like Foo and Bar. The difference from #159 lies in using b as a closure. My intension is to show the difference between closures with and without @autoclosure.

$1.isInout
? "&p\($0)"
: $1.isAutoclosure
? "p\($0)()"
Copy link
Contributor Author

@xiii111 xiii111 Dec 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only () is OK because @autoclosure can be used only when there are no parameters.
Also inout expression cannot be used on @autoclosure so we don't need to consider the situation when $1.isInout && $1.isAutoclosure.

@xiii111 xiii111 marked this pull request as ready for review December 10, 2023 12:07
Copy link
Member

@mbrandonw mbrandonw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to PR this!

Copy link
Member

@mbrandonw mbrandonw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks @xiii111!

@mbrandonw mbrandonw merged commit a695db3 into pointfreeco:main Dec 11, 2023
@xiii111
Copy link
Contributor Author

xiii111 commented Dec 12, 2023

@mbrandonw Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@DependencyClient and @autoclosure parameters

2 participants