Swift generics, same function name for array vs single object possible? - swift

Is it possible to write a generic function that returns type T and one with the same name that returns [T] ?
Example:
static func processResult<T: Decodable>(_ inputData: Data) -> [T] { ... }
static func processResult<T: Decodable>(_ inputData: Data) -> T { ... }
So when calling I can do:
let myObj: Car = processResult(data)
...
let myObjs: [Car] = processResult(data)
I presume that technically [T] can also be considered a T type, hence the compiler getting confused. There isn't a way of saying that T will NOT be an array type?

When you have a chance to set the constraints in the function definition, you may also do this:
func processResult<T: Decodable & Collection>(_ inputData: Data) -> T { ... }
func processResult<T: Decodable>(_ inputData: Data) -> T { ... }
The compiler will always select the "most specialised" candidate. So, if your result type conforms to Collection, it's always the first candidate that gets selected.
Otherwise, if finds the other candidate.

There isn't a way of saying that T will NOT be an array type?
Well, if you can a find a protocol P to which Array doesn't conform, but all the other Ts that you want to use the second overload with does conform to, then you can do:
func processResult<T: Decodable>(_ inputData: Data) -> [T] {
...
}
func processResult<T: Decodable & P>(_ inputData: Data) -> T {
...
}
struct SomeStruct : Decodable, P { ... }
let x: SomeStruct = processResult(someData) // second overload
let y: [SomeStruct] = processResult(someData) // first overload
Is it possible to write a generic function that returns type T and one with the same name that returns [T]?
Of course, you just need to pass in the type parameter explicitly to help the compiler infer what T is:
func processResult<T: Decodable>(_ inputData: Data, _ type: T.Type) -> [T] {
...
}
func processResult<T: Decodable>(_ inputData: Data, _ type: T.Type) -> T {
...
}
struct SomeStruct : Decodable { ... }
let x: SomeStruct = processResult(someData, SomeStruct.self) // second overload
let y: [SomeStruct] = processResult(someData, SomeStruct.self) // first overload
You might think that saying SomeStruct twice is long-winded, but it is very necessary here - without the first SomeStruct, it is ambiguous which overload you want to call, because the two overloads differ only in return type. Without the second SomeStruct, it is ambiguous what T is, because as you said, T can be an array too.

Yes, it is possible but you don't need to return [T], you can return only T, but you have to cast it like [Car]
func processResult<T: Decodable>(_ inputData: Data) -> T {
return try! JSONDecoder().decode(T.self, from: inputData)
}
struct Car: Decodable {
}
let cars: [Car] = processResult(Data())
let car: Car = processResult(Data())

As you can see in the Swift Language Source Code,
extension Array: Decodable where Element: Decodable { ... }
Array will be a Decodable if all its elements are Decodable. So if you try use generics, compiler only sees the protocol you are passing to the function as the T and that is the Decodable in your case.
So T will be seen as some Decodable
Also, [T] will be seen as some Decodable
To work around this:
You can mask the Swift.Decodable:
protocol Decodable: Swift.Decodable {}
So you code should work without need to any other changes.
p.s: I think Swift should infer it from the left side of an assignment operator or from the right side of a type casting automatically! But I know Swift developers are working hard to make these things happen and even one big enhancement of type inference is coming up with the Swift 5.4 after all these years!

Related

Swift protocol conformance when returning a generic

Here's an example:
protocol Feed {
func items<T>() -> [T]? where T: FeedItem
}
protocol FeedItem {}
class FeedModel: Feed, Decodable {
func items<T>() -> [T]? where T : FeedItem {
return [FeedItemModel]() // Error: Cannot convert return expression of type '[FeedItemModel]' to return type '[T]?'
}
}
class FeedItemModel: FeedItem, Decodable {}
Why does it:
A) try to convert to T when T is a generic, not a type?
B) does not recognize FeedItemModel as conforming to FeedItem?
func items<T>() -> [T]? where T : FeedItem
This says that the caller can define T to be whatever they want, as long as T conforms to FeedItemModel, and this function will return an optional array of those.
FeedItemModel is something that conforms to FeedItem, but it is not promised to be the type T that the caller requested.
As an example, consider:
class OtherModel: FeedItem {}
According to your function signature, I can do this:
let ms: [OtherModel]? = FeedModel().items()
But your function won't then return [OtherModel]? to me. I suspect you don't actually mean this to be generic. I expect you mean:
func items() -> [FeedItemModel]?
or possibly
func items() -> [FeedItem]?
(Though I would think very hard before doing the latter one and make sure that the protocol existential is really doing useful work here.)
A)
T is a type, a homogenous concrete type specified at runtime.
Imaging T is class Foo : FeedItem it's obvious that FeedItemModel cannot be converted to Foo
B)
FeedItemModel is recognized as conforming to FeedItem but this is irrelevant.
It's often a mix-up of generics and protocols. Generic types are not covariant. If you need covariant types use an associated type.
Either you can ignore generics because because it only applies to that one function and it isn't needed since directly saying that the return type is [FeedItem]? yields the same result
protocol Feed {
func items() -> [FeedItem]?
}
class FeedModel: Feed, Decodable {
func items() -> [FeedItem]? {
return [OtherModel]()
}
}
If you on the other hand want a generic protocol then you should use a associated type
protocol Feed2 {
associatedtype T: FeedItem
func items() -> [T]?
}
class FeedModel2: Feed2, Decodable {
typealias T = FeedItemModel
func items() -> [T]? {
return [FeedItemModel]()
}
}

Using Swift Generics to Call a Non-Generic Library

I'm trying to write a generic Swift wrapper for some of the vector operations in the Accelerate vDSP framework and I'm running into a problem calling the functions in a generic way.
My vector struct looks like:
public struct Vector<T> {
let array: [T]
public static func add(_ a: [T], _ b: [T]) -> [T] {
vDSP.add(a, b)
}
public static func + (_ lhs: Self , _ rhs: Self) -> Self {
Self.add(lhs.array, rhs.array)
}
}
The problem is the add function is overloaded to either take Floats and return Floats or take Doubles and return Doubles. Since the type isn't known at compile time I get an error No exact matches in call to static method 'add'
The only way I've found to get around this is to explicitly check the type before the call and cast:
public static func add(_ a: [T], _ b: [T]) -> [T] {
if T.self is Float.Type {
return vDSP.add(a as! [Float], b as! [Float]) as! [T]
} else {
return vDSP.add(a as! [Double], b as! [Double]) as! [T]
}
}
or to use constrained methods
public static func add(_ a: T, _ b: [T]) -> [T] where T == Float { vDSP.add(a, b) }
public static func add(_ a: T, _ b: [T]) -> [T] where T == Double { vDSP.add(a, b) }
Both of these lead to uncomfortable code duplication, and what's more if I had more than two types (for example if supported is added for the upcoming Float16 type) I'd need to keep adding more and more cases. The latter approach seems especially bad since the method bodies are identical.
I'd like to be able to do something like vDSP.add<T>(a, b) but it seems Swift doesn't support this. Is there some other way to acheive this and avoid the code duplication?

Swift: constrain a generic parameter for use in a more constrained generic function

I'm implementing a class which needs to conform to a protocol that looks like this:
protocol P {
func magic<T>(_ thing: Thing, as type: T.Type) -> T
}
This protocol is provided by third-party code, which means I can't change it in any way in order to solve this problem.
Now, I have a generic function
func magicForObject<T: AnyObject>(_ thing: Thing, as type: T.Type) -> T
and I want to call it from within my implementation of magic, just for input things which are in fact objects.
That is, I want to do something like this:
func magic<T>(_ thing: Thing, as type: T.Type) -> T {
if T.self is AnyClass {
return magicForObject(thing, as: type)
}
else {
// do something else
}
}
but I can't find any way to make this work. The code above obviously doesn't compile, and neither does stuff like
if let T_ = T.self as? AnyClass { ... }
because T_ is just a normal variable, not a generic parameter (which is presumably compile-time).
I also tried doing this:
func magic<T: AnyObject>(_ thing: Thing, as type: T.Type) -> T { ... }
func magic<T>(_ thing: Thing, as type: T.Type) -> T { ... }
and implementing the two separately. The constrained AnyObject one is correctly called if calling this function directly on the object, but not when the object is cast to protocol type P, in which case the second one is always used.
This situation seems hopelessly constrained, but are there any workarounds I haven't thought of?
Update
It looks like this isn't currently possible in Swift. I've made a post pitching the idea in the Swift forums; please feel free to chime in if this is something you also need.
Your example is kind of hard to work with, so I had to do a lot of assumptions, but I guess this should do it for what you are requiring.
From what you said you have a given protocol P:
protocol P {
func magic<T>(_ thing: Thing, as type: T.Type) -> T
}
Lets give P a default implementation of what you need it to do:
extension P {
// Implementation for magic where T is a class
func magic<T>(_ thing: Thing, as type: T.Type) -> T where T: AnyObject {
print("AnyObject Called")
return Test2() as! T
}
// Implementation for magic where T is a struct
func magic<T>(_ thing: Thing, as type: T.Type) -> T {
print("Struct Called")
return Test() as! T
}
}
You have a class that will conform to P
class Test2: P {
}
Lets assume you have this Thing object and a struct we want to pass to see if we have the right results:
class Thing {
}
struct Test {
}
Now lets test if we call magic on Test2 if it will call the right function accordingly to what type is passed to magic
let test = Test()
let test2 = Test2()
// T is Test2 so its a class
test2.magic(Thing(), as: Test2.self)
// T is Test so its a struct
test2.magic(Thing(), as: Test.self)
The Print Output calls
AnyObject Called
Struct Called
which seems like you could do something for structs and another thing for classes

Swift instantiate from generic type

I have a class implementing Mappable protocol and I want to pass the type as parameter to a function and get an instance of that object, but the type might also be a primitive type.
class MyMappable : Mappable {
required init?(map: Map) {
print("i am custom mappable")
}
}
func getInstance<T>() -> T{
if T.self == Int.self {
return 10 as T
}
if T.self is Mappable.self {
return T(Json : [:]) //this is the problem, T is not Mappable
}
}
What I tried is create 2 override for getInstance like this :
func getInstance<T>() -> T{}
func getInstance<T : Mappable>() -> T{}
but for more types it can be lots of override.
is there a way to let swift know T is mappable in first function?
You can conditionally cast it to a Mappable like this (untested):
if let MappableType = T.self as? Mappable.self {
return MappableType(Json : [:])
}
Usually variable names are lowercase but in this case I can imagine using uppercase since it is used as a type.
You could also do it differently depending on your usage/callsite.
How about constraining the getInstance<T>() -> T method:
getInstance<T: Mappable>() -> T

Generic function taking a type name in Swift

In C#, it's possible to call a generic method by specifying the type:
public T f<T>()
{
return something as T
}
var x = f<string>()
Swift doesn't allow you to specialize a generic method when calling it. The compiler wants to rely on type inference, so this is not possible:
func f<T>() -> T? {
return something as T?
}
let x = f<String>() // not allowed in Swift
What I need is a way to pass a type to a function and that function returning an object of that type, using generics
This works, but it's not a good fit for what I want to do:
let x = f() as String?
EDIT (CLARIFICATION)
I've probably not been very clear about what the question actually is, it's all about a simpler syntax for calling a function that returns a given type (any type).
As a simple example, let's say you have an array of Any and you create a function that returns the first element of a given type:
// returns the first element in the array of that type
func findFirst<T>(array: [Any]) -> T? {
return array.filter() { $0 is T }.first as? T
}
You can call this function like this:
let array = [something,something,something,...]
let x = findFirst(array) as String?
That's pretty simple, but what if the type returned is some protocol with a method and you want to call the method on the returned object:
(findFirst(array) as MyProtocol?)?.SomeMethodInMyProtocol()
(findFirst(array) as OtherProtocol?)?.SomeMethodInOtherProtocol()
That syntax is just awkward. In C# (which is just as strongly typed as Swift), you can do this:
findFirst<MyProtocol>(array).SomeMethodInMyProtocol();
Sadly, that's not possible in Swift.
So the question is: is there a way to accomplish this with a cleaner (less awkward) syntax.
Unfortunately, you cannot explicitly define the type of a generic function (by using the <...> syntax on it). However, you can provide a generic metatype (T.Type) as an argument to the function in order to allow Swift to infer the generic type of the function, as Roman has said.
For your specific example, you'll want your function to look something like this:
func findFirst<T>(in array: [Any], ofType _: T.Type) -> T? {
return array.lazy.compactMap { $0 as? T }.first
}
Here we're using compactMap(_:) in order to get a sequence of elements that were successfully cast to T, and then first to get the first element of that sequence. We're also using lazy so that we can stop evaluating elements after finding the first.
Example usage:
protocol SomeProtocol {
func doSomething()
}
protocol AnotherProtocol {
func somethingElse()
}
extension String : SomeProtocol {
func doSomething() {
print("success:", self)
}
}
let a: [Any] = [5, "str", 6.7]
// Outputs "success: str", as the second element is castable to SomeProtocol.
findFirst(in: a, ofType: SomeProtocol.self)?.doSomething()
// Doesn't output anything, as none of the elements conform to AnotherProtocol.
findFirst(in: a, ofType: AnotherProtocol.self)?.somethingElse()
Note that you have to use .self in order to refer to the metatype of a specific type (in this case, SomeProtocol). Perhaps not a slick as the syntax you were aiming for, but I think it's about as good as you're going to get.
Although it's worth noting in this case that the function would be better placed in an extension of Sequence:
extension Sequence {
func first<T>(ofType _: T.Type) -> T? {
// Unfortunately we can't easily use lazy.compactMap { $0 as? T }.first
// here, as LazyMapSequence doesn't have a 'first' property (we'd have to
// get the iterator and call next(), but at that point we might as well
// do a for loop)
for element in self {
if let element = element as? T {
return element
}
}
return nil
}
}
let a: [Any] = [5, "str", 6.7]
print(a.first(ofType: String.self) as Any) // Optional("str")
What you probably need to do is create a protocol that looks something like this:
protocol SomeProtocol {
init()
func someProtocolMethod()
}
And then add T.Type as a parameter in your method:
func f<T: SomeProtocol>(t: T.Type) -> T {
return T()
}
Then assuming you have a type that conforms to SomeProtocol like this:
struct MyType: SomeProtocol {
init() { }
func someProtocolMethod() { }
}
You can then call your function like this:
f(MyType.self).someProtocolMethod()
Like others have noted, this seems like a convoluted way to do what you want. If you know the type, for example, you could just write:
MyType().someProtocolMethod()
There is no need for f.