Swift Cast to Generic Type with Constraint - swift

I am using Swift 3 with constrained generics (i.e. a where clause). I have a problem when I am trying to do generic type casting. Here is a simplified example of the problem:
func jsonToObj<T:DomainResource>(jsonStr: String) -> [T:DomainResource] {
let json = JSON(parseJSON: jsonStr).dictionaryObject
let bundle = SMART.Bundle(json: json)
let result = bundle.entry?.map() {
return $0.resource as! T
}
return result!
}
My problem is when I return from the method, the compiler complains its cannot convert type [T] to type [T:DomainResource]. If I remove the DomainResource constraint from the generic, it compiles and runs just fine.
That's not what I want, so, I tried this:
let result = bundle.entry?.map() {
return $0.resource as! T:DomainResource
}
Swift doesn't seem to know what that means. Any idea on how to work around this problem? I'd like to not just cast them all to DomainResource objects, if possible.

You wrote this function signature:
func jsonToObj<T:DomainResource>(jsonStr: String) -> [T:DomainResource]
This says that the jsonToObj(jsonStr:) method returns a dictionary whose keys are of type T and whose values are of type DomainResource. It looks like you just want to write this function signature:
func jsonToObj<T:DomainResource>(jsonStr: String) -> [T]

Related

Why are where clauses only valid on functions with generic parameters?

It seems absurd that this method signature does not compile in Swift 4:
class Bar<ValueType> {
func version() throws -> String where ValueType == [String: Any] { ... }
}
(Error: where clause cannot be attached to a non-generic declaration)
but this compiles fine:
class Bar<ValueType> {
func version<T>(_ foo: T? = nil) throws -> String where ValueType == [String: Any] { ... }
}
Anyone have insight as to why this is the case?
Because ValueType has nothing to do with this method (in the first example). It would be wrong to put such a method in a type (class/struct/enum), since it's not really a true member of that type. It's conditionally a member of that type, depending on the truth value of the where clause.
To achieve this, you would want to put this method in an extension of your type, with the where clause you want. E.g.
extension YourType where ValueType == [String: Any] {
func version() throws -> String { ... }
}
That has been finally allowed in Swift 5.3. See Contextual Where Clauses in the official Language Guide.
Citing from there:
You can write a generic where clause as part of a declaration that doesn’t have its own generic type constraints, when you’re already working in the context of generic types. For example, you can write a generic where clause on a subscript of a generic type or on a method in an extension to a generic type.
...
extension Container {
func average() -> Double where Item == Int {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
That compiles just fine in Xcode 12 beta 4, but won't work in Xcode 11.6 that is shipped with Swift 5.2.4.

Why use generics when you can just use types [duplicate]

This question already has answers here:
Generics type constraint vs inheritance
(1 answer)
What is the in-practice difference between generic and protocol-typed function parameters?
(2 answers)
Closed 5 years ago.
Let's say I have a protocol:
protocol Amazing {
func doAmazingStuff()
}
And then I have a function that takes that type to do things with it:
func doMoreCoolThings<T: Amazing>(awesome: T) -> T { ... }
What's the difference between that and just doing something like this?
func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }
My question here is why do we even bother using generics when we can just put in the type like that?
UPDATE
So I can see the point in using genetics instead of the:
func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }
However is there really any use to using that function or would it always be better to use generics?
This is just one benefit that I can immediately think of. I'm sure there are lots more.
Let's say We have two classes A and B that both conform to Amazing.
If we pass A() into this function:
func doMoreCoolThings<T: Amazing>(awesome: T) -> T { ... }
like this:
let val = doMoreCoolThings(awesome: A())
We are sure that val is of type A and the compiler knows that too. This means we can access A's members using val.
On the other hand if we pass A() into this function:
func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }
like this:
let val = doMoreCoolThings(awesome: A())
val's compile time type is Amazing. The compiler does not know what type of Amazing it is. Is it A or is it B or is it something else? The compiler doesn't know. You will have to cast the result to A in order to access its members.
let a = val as! A
The cast is also not guaranteed to succeed.
If you put these casts everywhere your code will soon become very messy.
Your question contains the false premise that
func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }
is equivalent to:
func doMoreCoolThings<T: Amazing>(awesome: T) -> T { ... }
This is not at all true. Say I have these conforming types to work with:
struct AmazingApple: Amazing {
func doAmazingStuff() { print("I'm an apple who talks!") }
}
struct AmazingBanana: Amazing {
func doAmazingStuff() { print("I'm a banana who talks!") }
}
Look at what the first piece of code let's me do:
func doMoreCoolThings(awesome: Amazing) -> Amazing {
return AmazingBanana()
}
let input = AmazingApple()
let result = doMoreCoolThings(awesome: input)
print("The input is of type \(type(of: input))")
print("The return is of type: \(type(of: result))")
The parameter type and return type are different, even though they're both subtypes of Amazing.
On the other hand, looks what happens when you try to do the same with the generic variant:
Generics are used to express relationships between types.
The non-generic code expresses:
"Let doMoreCoolThings(awesome:) be a function that takes some value whose type is a subtype of Awesome, and returns a value whose type is a subtype of Awesome."
The generic code expresses:
"Let doMoreCoolThings(awesome:) be a function that takes some value whose type is a subtype of some type, call it T, and returns a value those type is a subtype of T, where T itself is a subtype of Amazing."
Notice that the second quote expresses the requirement for the parameter and the return to be of the same type. It's not sufficient to just both have them be subtypes of Amazing.

Return any type from a function in Swift

I am attempting to create a function that can return any type. I do not want it to return an object of type Any, but of other types, i.e. String, Bool, Int, etc. You get the idea.
You can easily do this using generics in this fashion:
func example<T>(_ arg: T) -> T {
// Stuff here
}
But is it possible to do it without passing in any arguments of the same type? Here is what I am thinking of:
func example<T>() -> T {
// Stuff here
}
When I try to do this, everything works until I call the function, then I get this error:
generic parameter 'T' could not be inferred
is it possible to do it without passing in any arguments of the same type?
The answer is yes, but there needs to be a way for the compiler to infer the correct version of the generic function. If it knows what it is assigning the result to, it will work. So for instance, you could explicitly type a let or var declaration. The below works in a playground on Swift 3.
protocol Fooable
{
init()
}
extension Int: Fooable {}
extension String: Fooable {}
func foo<T: Fooable>() -> T
{
return T()
}
let x: String = foo() // x is assigned the empty string
let y: Int = foo() // y is assigned 0

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.

Why doesn't swift infer the appropriate overload function with a generic return argument without a type constraint?

Note
Swift is changing rapidly, this question was asked regarding:
Xcode 7, Swift 2.0
Explanation
I'm looking to implement a generic return argument. Quite often, I find it necessary to implement an optional version overload so I can access the underlying type and handle it appropriately. Here's some manufactured functions. The assignment of String is just there as a placeholder for replication:
func ambiguous<T>() -> T {
let thing = "asdf"
return thing as! T
}
func ambiguous<T>() -> T? {
return nil
}
Now, if we look at the implementation:
// Fine
let a: String = ambiguous()
// Ambiguous
let b: String? = ambiguous()
This might seem obvious because you could assign type T to a variable of type T?. So it makes sense that it would have trouble inferring. The problem is, that with a type constraint, it suddenly works. (This can be anything, I'm using Equatable for easy replication.
func nonAmbiguous<T : Equatable>() -> T {
let thing: AnyObject = "asdf"
return thing as! T
}
func nonAmbiguous<T : Equatable>() -> T? {
return nil
}
And now, it functions as expected:
// Fine
let c: String = nonAmbiguous()
// Fine
let d: String? = nonAmbiguous()
Note, this also works with other type:
func nonAmbiguous<T>() -> [T] {
let thing: AnyObject = ["asdf"]
return thing as! [T]
}
func nonAmbiguous<T>() -> [T]? {
return nil
}
// Fine
let e: [String] = nonAmbiguous()
// Fine
let d: [String]? = nonAmbiguous()
Question:
Is there a way to have a return generic argument infer the appropriate overload through optionality?
if no
Is this a language feature, or a bug somewhere. If it's a language feature, please explain the underlying issue preventing the possibility of this behavior.
The first example is ambiguous because T can be inferred as both String
and String?.
The second example is not ambiguous because String is Equatable but String? is not, so T : Equatable cannot be inferred as String?.
The third case is not ambiguous because [T] is not
inferred as [String]?.
Remark: Generally, Optional<Wrapped> does not conform to Equatable
even if Wrapped does, in the same way as Array<Element>
does not conform to Equatable even if Element does.
This is a restriction of the current type system in Swift which
might be improved in a future version, compare
[swift-dev] RFC: Adding Optional variants of == for collections to the std lib.
from the Swift development mailing list.