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

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."

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 can I restore type generic type inference in Swift result builders?

When designing result builders which consume and product generic types, I often find myself needing to spell out types in an annoyingly explicit way. Consider this toy example:
#resultBuilder enum SomeBuilder {
static func buildBlock(_ v: Result<Int,Error>) -> Result<Int,Error> { v }
}
struct SomeStruct {
init(#SomeBuilder _ v: () -> Result<Int,Error>) {}
}
let x = SomeStruct {
Result.success(0)
}
SomeBuilder is almost the simplest possible #resultBuilder: it takes in single values of type Result<Int,Error> and returns them as-is. SomeStruct is a simple struct with an initializer that takes a single #SomeBuilder closure which produces Result<Int,Error>. Consequently, it makes sense to me that this code should compile. Swift should be able to figure out that Result.success(0) refers to Result<Int,Error>.success(0) because this is the only type that could make sense in this context. However, this example fails to compile with
_:9:5: error: cannot convert value of type 'Result<Int, _>' to expected argument type 'Result<Int, Error>'
Result.success(0)
^
_:9:5: note: arguments to generic parameter 'Failure' ('_' and 'Error') are expected to be equal
Result.success(0)
^
_:9:5: error: generic parameter 'Failure' could not be inferred
Result.success(0)
^
_:9:5: note: explicitly specify the generic arguments to fix this issue
Result.success(0)
^
<Int, <#Failure: Error#>>
On the same line, Swift claims that it cannot infer the Failure type of Result.success(0) and then claims that it knows the failure must be Error. I can get this code to compile by using
let x = SomeStruct {
Result<Int,Error>.success(0)
}
but in real examples, being this verbose with the types is extremely inhibitive to the DSLs I want to design.
Is this a bug in the compiler? How can I get Swift to infer generic types in result builders such as this one?
I am fairly confident that this should be classified as a bug, but I do have an explanation and a solution. Consider how Swift transforms this result builder.
let x = SomeStruct {
Result.success(0)
}
becomes (more-or-less)
let x = SomeStruct {
let v0 = Result.success(0)
return SomeBuilder.buildBlock(v0)
}
In isolation, there is no way for the compiler to infer the type of v0. This is only known once it is used as a parameter for buildBlock, but Swift does not infer types of declarations from their later usage.
However, if we add a trivial buildExpression to SomeBuilder, this transformation is different.
#resultBuilder enum SomeBuilder {
static func buildExpression(_ v: Result<Int,Error>) -> Result<Int,Error> { v }
static func buildBlock(_ v: Result<Int,Error>) -> Result<Int,Error> { v }
}
Now, Swift transforms
let x = SomeStruct {
Result.success(0)
}
into (more-or-less)
let x = SomeStruct {
let v0 = SomeBuilder.buildExpression(Result.success(0))
return SomeBuilder.buildBlock(v0)
}
In this code, Swift is able to infer that the type of v0 is Result<Int,Error> because that is the type that buildExpression returns, and Swift is able to infer that the type of Result.success(0) is Result<Int,Error> because this is the type that buildExpression takes as an argument.
For more details on what these #resultBuilder transformations look like, see this incredible WWDC session about result builders.
Conclusion
#resultBuilder enum SomeBuilder {
static func buildExpression(_ v: Result<Int,Error>) -> Result<Int,Error> { v }
static func buildBlock(_ v: Result<Int,Error>) -> Result<Int,Error> { v }
}
struct SomeStruct {
init(#SomeBuilder _ v: () -> Result<Int,Error>) {}
}
let x = SomeStruct {
Result.success(0)
}
compiles successfully. If you want generic type inference to work in result builders like you're used to in other parts of the language, you need to have a buildExpression taking the relevant type, even if it just returns its input and even if you otherwise would not need any buildExpressions at all! Once you have done this, your result builders will be much more readable as you will not need to unnecessarily spell out types when they are clear from context. This works in complicated builders and not just in toy examples such as this one.

Swift Implementation generic function

I'm a developer on Java and I'm trying to write in Swift the same solution that I have in Java code.
Is it possible to do this on Swift?
Example Java:
public interface Converter<S,T> {
T convert(S in)
}
public class CarConverterToDTO implements Converter<Car, CarDTO> {
#Override
public CarDTO convert(Car in) {
.....
}
}
Example Swift:
protocol Converter {
func convert<IN, OUT>(in: IN) -> OUT
}
How it would be the implementation?
Thanks!!!
What appears to be a simple question is actually the tip of a rather large and unpleasant iceberg…
I'm going to start by giving you what is probably the real solution to your problem:
class Converter<Input, Output> {
func convert(_ input: Input) -> Output {
fatalError("subclass responsibility")
}
}
struct Car { }
struct CarDTO { }
class DTOCarConverter: Converter<Car, CarDTO> {
override func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}
Above, I've translated your Java Converter interface into a Swift class instead of a Swift protocol. That's probably what you want.
Now I'll explain why.
A programmer coming from Java to Swift might think that a Swift protocol is the equivalent of a Java interface. So you might write this:
protocol Converter {
associatedtype Input
associatedtype Output
func convert(_ input: Input) -> Output
}
struct Car { }
struct CarDTO { }
class /* or struct */ DTOCarConverter: Converter {
func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}
Okay, now you can create a converter and convert something:
let converter = DTOCarConverter()
let car = Car()
let dto = converter.convert(car)
But you're going to run into a problem as soon as you want to write a function that takes a Converter as an argument:
func useConverter(_ converter: Converter) { }
// ^
// error: protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements
“Well, duh,” you say, “you forgot the type arguments!” But no, I didn't. Swift doesn't allow explicit type arguments after a protocol name:
func useConverter(_ converter: Converter<Car, CarDTO>) { }
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'
I don't want to get into why you can't do this. Just accept that a Swift protocol is not generally equivalent to a Java interface.
A Swift protocol with no associated types and no mention of Self is, generally, equivalent to a non-generic Java interface. But a Swift protocol with associated types (or that mentions Self) is not really equivalent to any Java construct.
When discussing this problem, we often use the acronym “PAT”, which stands for “Protocol with Associated Types” (and includes protocols that mention Self). A PAT doesn't define a type that you can use as a function argument, return value, or property value. There's not much you can do with a PAT:
You can define a subprotocol. For example, Equatable is a PAT because it defines the == operator to take two arguments of type Self. Hashable is a subprotocol of Equatable.
You can use a PAT as a type constraint. For example, Set is a generic type. Set's type parameter is named Element. Set constrains its Element to be Hashable.
So you can't write a function that takes a plain Converter as an argument. But you can write a function that takes any implementation of Converter as an argument, by making the function generic:
func useConverter<MyConverter: Converter>(_ converter: MyConverter)
where MyConverter.Input == Car, MyConverter.Output == CarDTO
{ }
That compiles just fine. But sometimes it's inconvenient to make your function generic.
And there's another problem that this doesn't solve. You might want a container that holds various Converters from Car to CarDTO. That is, you might want something like this:
var converters: [Converter<Car, CarDTO>] = []
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'
We can't fix this by making converters generic, like we did with the useConverter function.
What you end up needing is a “type-erased wrapper”. Note that “type-erased” here has a different meaning that Java's “type erasure”. In fact it's almost the opposite of Java's type erasure. Let me explain.
If you look in the Swift standard library, you'll find types whose names start with Any, like AnyCollection. These are mostly “type-erased wrappers” for PATs. An AnyCollection conforms to Collection (which is a PAT), and wraps any type that conforms to Collection. For example:
var carArray = Array<Car>()
let carDictionary = Dictionary<String, Car>()
let carValues = carDictionary.values
// carValues has type Dictionary<String, Car>.Values, which is not an array but conforms to Collection
// This doesn't compile:
carArray = carValues
// ^~~~~~~~~
// error: cannot assign value of type 'Dictionary<String, Car>.Values' to type '[Car]'
// But we can wrap both carArray and carValues in AnyCollection:
var anyCars: AnyCollection<Car> = AnyCollection(carArray)
anyCars = AnyCollection(carValues)
Note that we have to explicitly wrap our other collections in AnyCollection. The wrapping is not automatic.
Here's why I say this is almost the opposite of Java's type erasure:
Java preserves the generic type but erases the type parameter. A java.util.ArrayList<Car> in your source code turns into a java.util.ArrayList<_> at runtime, and a java.util.ArrayList<Truck> also becomes a java.util.ArrayList<_> at runtime. In both cases, we preserve the container type (ArrayList) but erase the element type (Car or Truck).
The Swift type-erasing wrapper erases the generic type but preserves the type parameter. We turn an Array<Car> into an AnyCollection<Car>. We also turn a Dictionary<String, Car>.Values into an AnyCollection<Car>. In both cases, we lose the original container type (Array or Dictionary.Values) but preserve the element type (Car).
So anyway, for your Converter type, one solution to storing Converters in a container is to write an AnyConverter type-erased wrapper. For example:
struct AnyConverter<Input, Output>: Converter {
init<Wrapped: Converter>(_ wrapped: Wrapped) where Wrapped.Input == Input, Wrapped.Output == Output {
self.convertFunction = { wrapped.convert($0) }
}
func convert(_ input: Input) -> Output { return convertFunction(input) }
private let convertFunction: (Input) -> Output
}
(There are multiple ways to implement type-erased wrappers. That is just one way.)
You can then use AnyConverter in property types and function arguments, like this:
var converters: [AnyConverter<Car, CarDTO>] = [AnyConverter(converter)]
func useConverters(_ converters: [AnyConverter<Car, CarDTO>]) {
let car = Car()
for c in converters {
print("dto = \(c.convert(car))")
}
}
But now you should ask: what's the point? Why bother making Converter a protocol at all, if I'm going to have to use a type-erased wrapper? Why not just use a base class to define the interface, with subclasses implementing it? Or a struct with some closures provided at initialization (like the AnyConverter example above)?
Sometimes, there's not a good reason to use a protocol, and it's more sensible to just use a class hierarchy or a struct. So you should take a good look at how you're implementing and using your Converter type and see if a non-protocol approach is simpler. If it is, try a design like I showed at the top of this answer: a base class defining the interface, and subclasses implementing it.
The Swift equivalent to your Java code looks like this.
protocol Converter {
associatedtype Input
associatedtype Output
func convert(input: Input) -> Output
}
class CarConverterToDTO: Converter {
typealias Input = Car
typealias Output = CarDTO
func convert(input: Car) -> CarDTO {
return CarDTO()
}
}
Explanation
The equivalent to a generic Java interface in Swift, would be a protocol with associatedtypes.
protocol Converter {
associatedtype Input
associatedtype Output
}
To create an implementation of that protocol, the implementation must specify which types the associated types maps to, using typealias.
class CarConverterToDTO: Converter {
typealias Input = Car
typealias Output = CarDTO
}
Type Erasure
If you try to use to this approach, you may run into the issue of trying to store an instance of your generic protocol in a variable or property, in which case you will get the compiler error:
protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements
The way to solve this issue in Swift, is by using type erasure, where you create a new implementation of your generic protocol, that itself is a generic type (struct or class), and uses a constructor accepting a generic argument, matching your protocol, like so:
struct AnyConverter<Input, Output>: Converter {
// We don't need to specify type aliases for associated types, when the type
// itself has generic parameters, whose name matches the associated types.
/// A reference to the `convert(input:)` method of a converter.
private let _convert: (Input) -> Output
init<C>(_ converter: C) where C: Converter, C.Input == Input, C.Output == Output {
self._convert = converter.convert(input:)
}
func convert(input: Input) -> Output {
return self._convert(input)
}
}
This is usually accompanied by an extension function on the generic protocol, that performs the type erasure by creating an instance of AnyConverter<Input, Output> using self, like so:
extension Converter {
func asConverter() -> AnyConverter<Input, Output> {
return AnyConverter(self)
}
}
Using type erasure, you can now create code that accepts a generic Converter (by using AnyConverter<Input, Output>), that maps Car to CarDTO:
let car: Car = ...
let converter: AnyConverter<Car, CarDTO> = ...
let dto: CarDTO = converter.convert(input: car)

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

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()
}

Swift not finding the correct type

I am trying to use SwiftHamcrest
I have a function
func equalToArray<T, S>(_ vector:Array<S>) -> Matcher<T> {
let v: Matcher<T> = Hamcrest.hasCount(16)
return v
}
This gives an error
Error:(16, 31) 'hasCount' produces 'Matcher<T>', not the expected contextual result type 'Matcher<T>'
SwiftHamcrest has two hasCount functions
public func hasCount<T: Collection>(_ matcher: Matcher<T.IndexDistance>) -> Matcher<T>
public func hasCount<T: Collection>(_ expectedCount: T.IndexDistance) -> Matcher<T>
Why is my code complaining isn't it returning the same type that is needed.
As a note and possibly a different question I had to add the Hamcrest. before the hasCount method call as otherwise it tried to match to the first function
What am I missing with types?
Your method equalToArray<T, S> does not know that T is a collection, so the result from the generic hasCount(...) methods above will not be assignable to v in your method (since these results returns Matcher<T> instances constrained to T:s that are Collection:s). I.e., v is of a type Matcher<T> for a non-constrained T, meaning, in the eyes of the compiler, there is e.g. no T.IndexDistance for the T of v:s type.
If you add a Collection type constraint to the T of your method, the assignment from hasCount(...) result to v should compile:
func equalToArray<T: Collection, S>(_ vector: Array<S>) -> Matcher<T> {
let v: Matcher<T> = Hamcrest.hasCount(16)
return v
}
In a perfect world, the compiler could've given us a more telling error message, say along the lines of
Error:(16, 31) 'hasCount' produces 'Matcher<T>' where 'T: Collection',
not the expected contextual result type 'Matcher<T>'
Now, I don't know what you're intending to test here, but as #Hamish points out, you might actually want to return a Matcher<[S]> and drop the T placeholder. E.g. using the count property of the supplied vector parameter as argument to hasCount(...)?
func equalToArray<S>(_ vector: Array<S>) -> Matcher<[S]> {
return hasCount(vector.count)
}
Not having used Hamcrest myself, I might be mistaken, but based on a quick skim over the SwiftHamcrest docs, I believe equalToArray(_:) defined as above would construct a matcher for "vector equality" (w.r.t. semantics of the function name) based only on the count of two vectors, in which case the following assert would be a success
let arr1 = ["foo", "bar"]
let arr2 = ["bar", "baz"]
assertThat(arr1, equalToArray(arr2)) // success! ...
But this is just a byline, as you haven't shown us the context where you intend to apply your equalToArray(_:) method/matcher; maybe you're only showing us a minimal example, whereas the actual body of you custom matcher is more true to the method's name.