-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support generic functions #52
Comments
I'm extremely bad at Swift Syntax, so I'm not sure I'm ready to take a stab at the solution myself, but I was thinking about how I'd solve this. Might not actually be helpful – just a thought. I think the wrapped type of, e.g., That would give you a somewhat unnatural but completely semantic † I think this is identical to just plain |
@arennow I think you're right about a solution having to revolve around the use of @Spyable
protocol MyProtocol {
func useGenerics<A, B, C>(one: A, two: B, three: C, four: String) -> (A, (B, C), String)
}
// Which generates
class MyProtocolSpy: MyProtocol {
var useGenericsOneTwoThreeFourCallsCount = 0
var useGenericsOneTwoThreeFourCalled: Bool {
return useGenericsOneTwoThreeFourCallsCount > 0
}
var useGenericsOneTwoThreeFourReceivedArguments: (one: Any, two: Any, three: Any, four: String)?
var useGenericsOneTwoThreeFourReceivedInvocations: [(one: Any, two: Any, three: Any, four: String)] = []
var useGenericsOneTwoThreeFourReturnValue: (Any, (Any, Any), String)!
var useGenericsOneTwoThreeFourClosure: ((Any, Any, Any, String) -> (Any, (Any, Any), String))?
func useGenerics<A, B, C>(one: A, two: B, three: C, four: String) -> (A, (B, C), String) {
useGenericsOneTwoThreeFourCallsCount += 1
useGenericsOneTwoThreeFourReceivedArguments = (one, two, three, four)
useGenericsOneTwoThreeFourReceivedInvocations.append((one, two, three, four))
if useGenericsOneTwoThreeFourClosure != nil {
return useGenericsOneTwoThreeFourClosure!(one, two, three, four) as! (A, (B, C), String)
} else {
return useGenericsOneTwoThreeFourReturnValue as! (A, (B, C), String)
}
}
} This compiles and would be usable, but it would require that users of the spy do type casting if they want to use these One way to improve usability could be to introduce nice guard let returnValue = useGenericsOneTwoThreeFourReturnValue as? (A, (B, C), String) else {
fatalError("\(useGenericsOneTwoThreeFourReturnValue) was of type \(type(of: useGenericsOneTwoThreeFourReturnValue)). Expected: (A, (B, C), String)")
}
return returnValue I'm currently exploring a PR that'll go with this approach. |
@dafurman In the case of unconstrained generics, like in your example, the only static type we could use is Consider the following situation: protocol User {
var name: String { get }
var friendCount: Int { get set }
}
@Spyable
protocol MyFakeThing {
func process<U: User>(user: U)
} You'd want to be able to do something like var spy: MyFakeThingSpy = // whatever
// Do something with it
let sumOfFriends = spy.processUserReceivedInvocations.map(\.friendCount).reduce(0, +) But right now you can't because the generated code makes reference to Currently generated invalid spy codeclass MyFakeThingSpy: MyFakeThing {
var processUserCallsCount = 0
var processUserCalled: Bool {
self.processUserCallsCount > 0
}
var processUserReceivedUser: U?
var processUserReceivedInvocations: [U] = []
var processUserClosure: ((U) -> Void)?
func process<U: User>(user: U) {
self.processUserCallsCount += 1
self.processUserReceivedUser = (user)
self.processUserReceivedInvocations.append((user))
self.processUserClosure?(user)
}
} If we instead replaced2 Hypothetical existential-using spy codeclass MyFakeThingSpy: MyFakeThing {
var processUserCallsCount = 0
var processUserCalled: Bool {
self.processUserCallsCount > 0
}
var processUserReceivedUser: (any User)?
var processUserReceivedInvocations: [any User] = []
var processUserClosure: ((any User) -> Void)?
func process<U: User>(user: U) { // This could also be `any User`, but it doesn't have to be
self.processUserCallsCount += 1
self.processUserReceivedUser = (user)
self.processUserReceivedInvocations.append((user))
self.processUserClosure?(user)
}
} Footnotes
|
@arennow One thing about
I like the idea, but I think we'd run into trouble if generic identifier names are reused, like the commonly used "T" for generics: protocol MyProtocol {
func process<T: User>(user: T)
func process<T: Customer>(customer: T)
} In this example, we'd want to |
Is your feature request related to a problem? Please describe.
Currently the generated spy doesn't handle generic functions:
Describe the solution you'd like
In this instance I would expect something like the following, though obviously conformances would change the types used.
Describe alternatives you've considered
N/A
Additional context
N/A
The text was updated successfully, but these errors were encountered: