Swift 4, Generics why do I need to cast to T here? - swift

When running this code in Swift 4, The compiler throws the following error:
"Cannot convert return expression of type 'Comment?' to return type '_?', when running this code in a playground:
import UIKit
class Comment {}
class Other {}
func itemForSelectedTabIndex<T>(index: Int, type: T.Type) -> T? {
return (type is Comment.Type) ? getComment() : getOther()
}
func getComment() -> Comment {
return Comment()
}
func getOther() -> Other {
return Other()
}
let thing = itemForSelectedTabIndex(index: 0, type: Other.self)
In order to make this work, I need to cast the return value as generic, like this:
return (type is Comment.Type) ? getComment() as! T : getOther() as! T
Could someone explain the logic behind this?
If the expected return value is a 'Generic', and basically it won't matter what type I return, why the compiler complains about it? Shouldn't this work without casting?

Generics aren't some magical wildcards that can just have any value at any time.
When you callitemForSelectedTabIndex(index: 0, type: Comment.self), T is inferred to Comment. Likewise, for Other.
When T has been inferred to Comment, the same value of T is consistent throughout everywhere it's used. Thus, the return value must be of type Comment (or a sub-type).
Another is with your expression (type is Comment.Type) ? getComment() : getOther(). There are 2 cases, and neither of them are valid:
type is Comment.Type: getComment() returns a Comment, a type that is compatible with the value of T, which is Comment. But, the two operands of the conditional operator have no common supertype. That's not valid.
type is not Comment.Type: getOther() returns an Other, which may or may not be compatible with T. All we know about T is that it is not comment. This doesn't mean it's necessarily Other. It can be any other type, like Int. Thus, this return expression fails.
What you need is a common supertype of both types you wish to return. Most probably, a protocol is the correct choice (rather than a shared superclass):
protocol TabItem {}
class Comment {}
class Other {}
func itemForSelectedTabIndex<T: TabItem>(index: Int, type: T.Type) -> TabItem {
return getCommentOrOtherOrSomethingElse()
}

Related

A question about parameter type and return type of buildBlock in #resultBuilder

According to the swift language guide, I need to implement buildBlock method when I define a result builder. And the guide also says:
"
static func buildBlock(_ components: Component...) -> Component
Combines an array of partial results into a single partial result. A result builder must implement this method.
"
Obviously, the parameter type and the return type should be the same. However, if I use different types, it also seems to be no problem. I write some simple code below:
#resultBuilder
struct DoubleBuilder {
static func buildBlock(_ components: Int) -> Double {
3
}
}
It compiles successfully in Xcode. Can the types be different?It seems like to be inconsistent with the official guide.
EDIT:
The parameter type of buildBlock method in ViewBuilder in SwiftUI is also different from the return type.
For example:
static func buildBlock<C0, C1>(C0, C1) -> TupleView<(C0, C1)>
When they say
static func buildBlock(_ components: Component...) -> Component
They don't strictly mean that there exists one type, Component, that buildBlock must take in and return. They just mean that buildBlock should take in a number of parameters, whose types are the types of "partial results", and return a type that is a type of a "partial result". Calling them both Component might be a bit confusing.
I think the Swift evolution proposal explains a little better (emphasis mine):
The typing here is subtle, as it often is in macro-like features. In the following descriptions, Expression stands for any type that is acceptable for an expression-statement to have (that is, a raw partial result), Component stands for any type that is acceptable for a partial or combined result to have, and FinalResult stands for any type that is acceptable to be ultimately returned by the transformed function.
So Component, Expression and FinalResult etc don't have to be fixed to a single type. You can even overload the buildXXX methods, as demonstrated later in the evolution proposal.
Ultimately, result builders undergo a transformation to calls to buildXXX, when you actually use them. That is when type errors, if you have them, will be reported.
For example,
#resultBuilder
struct MyBuilder {
static func buildBlock(_ p1: String, _ p2: String) -> Int {
1
}
}
func foo(#MyBuilder _ block: () -> Double) -> Double {
block()
}
foo { // Cannot convert value of type 'Int' to closure result type 'Double'
10 // Cannot convert value of type 'Int' to expected argument type 'String'
20 // Cannot convert value of type 'Int' to expected argument type 'String'
}
The first error is because the result builder's result type is Int, but foo's closure parameter expects a return value of type Double. The last two is because the transformation of the result builder attempts to call MyBuilder.buildBlock(10, 20).
It all makes sense if you look at the transformed closure:
foo {
let v1 = 10
let v2 = 20
return MyBuilder.buildBlock(v1, v2)
}
Essentially, as long as the transformed code compiles, there is no problem.
See more details about the transformation here.

How do you provide a default argument for a generic function with type constraints?

The following function definition is legal Swift:
func doSomething<T: StringProtocol>(value: T = "abc") {
// ...
}
The compiler is able to determine that the default argument "abc" is a String, and String conforms to StringProtocol.
This code however does not compile:
func doSomething<T: Collection>(value: T = "abc") where T.Element == Character {
// ...
}
The compiler error:
Default argument value of type 'String' cannot be converted to type 'T'
It seems as though the compiler would have just as much information as in the first case to determine that String is indeed convertible to T. Furthermore, if I remove the default argument and call the function with the same value, it works:
doSomething(value: "abc")
Can this function be written differently so that I can provide the default String argument? Is this a limitation of Swift, or simply a limitation of my mental model?
The significant constraint is T: ExpressibleByStringLiteral. That's what allows something to be initialized from a string literal.
func doSomething<T: Collection>(value: T = "abc")
where T.Element == Character, T: ExpressibleByStringLiteral {
// ...
}
As Leo Dabus notes, T.Element == Character is technically not necessary, but removing it changes the meaning. Just because something is a collection and can be initialized by a string literal does not mean that its elements are characters.
It's also worth noting that while all of this is possible, it generally is poor Swift IMO. Swift does not have any way to express what the default type is, so doSomething() in all of these cases causes "Generic parameter 'T' could not be inferred".
The correct solution IMO is an overload, which avoids all of these problems:
func doSomething<T: StringProtocol>(value: T) {
}
func doSomething() {
doSomething(value: "abc")
}
This allows you to make the default parameter not just "something that can be initialized with the literal "abc"", but what you really mean: the default value is the String "abc".
As a rule, default parameters are just conveniences for overloads, so you can generally replace any default parameter with an explicit overload that lacks that parameter.

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.

What does the "_" type mean in swift error messages?

Occasionally when using generics, I get an error message that refers to a "_" as a parameter. It doesn't appear to be documented. What does it mean?
As an example, I get the error:
Cannot convert value of type 'JawDroppingFeat<Superhero>' to closure result type 'JawDroppingFeat<_>'
when I try to compile:
protocol SuperheroType {
typealias Superpower
}
struct JawDroppingFeat<Superhero: SuperheroType where Superhero: Arbitrary, Superhero.Superpower: Arbitrary>: Arbitrary {
let subject: Superhero
let superpowerUsed: Superhero.Superpower
static var arbitrary: Gen<JawDroppingFeat<Superhero>> {
get {
return Gen.zip(Superhero.arbitrary, Superhero.Superpower.arbitrary)
.map{ (x: Superhero, y: Superhero.Superpower) in
JawDroppingFeat(subject: x, superpowerUsed: y)
}
}
}
}
The Gen and Arbitrary types are from SwiftCheck and the relevant declarations are:
public struct Gen<A> {
public static func zip<A, B>(gen1: SwiftCheck.Gen<A>, _ gen2: SwiftCheck.Gen<B>) -> SwiftCheck.Gen<(A, B)>
public func map<B>(f: A -> B) -> SwiftCheck.Gen<B>
}
public protocol Arbitrary {
public static var arbitrary: SwiftCheck.Gen<Self> { get }
}
I assume that the <_> is something to do with swift failing to infer the type parameter rather than an image of Chris Lattner wincing at me. But does it have a more precise (and documented) meaning?
Edit
My best current theory is that when Swift fails to infer a type parameter, instead of failing immediately, it assigns a null (_) type, which results in the actual compile error downstream at some point where an incompatible type (in my case, the parameters to .map) is passed.
It means you have incomplete type information in a return value of parameter.
In this case the .map function is returning a generic JawDroppingFeat for which you did not specify the embedded type.
I assume you meant to write
JawDroppingFeat<SuperHero>(subject: x, superpowerUsed: y)
In general, my experience is that this error message appears to be constructed backwards. "Cannot convert value of type X to Y" seems to mean "You supplied Y, but what's needed is X."

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.