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

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.

Related

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

How to express the type of a constrained-generic function in Swift?

I can define a function
func myGenericFunc<A: SomeProtocol>(_ a: A) -> A { ... }
Now I want to type a variable to hold exactly this kind of function, but I find I can't spell the type:
let f: (SomeProtocol) -> SomeProtocol // doesn't express the genericity
let f: <A: SomeProtocol>(A) -> A // non-existent syntax
Is there any way I can express this directly?
Note that in particular I want f to still be generic: it should accept any SomeProtocol conformer (so no fixing the generic type parameter in advance). In other words: anything I can do with myGenericFunc I want to also be able to do with f.
The quick answer is no, you can't use a single variable to hold different function implementations. A variable needs to hold something concrete for the compiler to allocate the proper memory layout, and it can't do it with a generic construct.
If you need to hold different closure references, you can use a generic type alias:
typealias MyGenericFunction<T: SomeProtocol> = (T) -> T
var f1: MyGenericFunction<SomeConformerType> // expanded to (SomeConformerType) -> SomeConformerType
var f2: MyGenericFunction<AnotherConformerType> // expanded to (AnotherConformerType) -> AnotherConformerType
My original question was about expressing the type directly, but it's also interesting to notice that this is impossible for a different reason:
func myGenericFunc<A: SomeProtocol>(_ a: A) -> A { ... }
let f = myGenericFunc // error: generic parameter 'A' could not be inferred
You could see this as the "deeper reason" there's no spelling for the generic type I wanted: a variable (as opposed to a function) simply cannot be generic in that sense.

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.

Swift Generics: Constraining Argument Types

Coming from a C++ background (templates), I'm struggling to understand why the following piece of Swift code (generics) does not compile:
func backwards<T>(array: [T]) -> [T] {
let reversedCollection = array.sort(>)
return reversedCollection
}
The way I understand it is that T is a generic type on which I do not put any constraint (<T>) and declare array to be of type Array<T>. Yet this produces the following error:
Ambiguous reference to member 'sort()'
I understand that constraints can be put on the generic type using protocols. However, in this case, I don't want any constraint on T. Rather, I want to constrain the type of the first parameter.
I've been reading Apple's documentation on Generic Types for a few hours now but I'm still not much wiser. I have the impression that this is not possible and constraints are put solely on the declared types, but that's as far as I got.
So the question is: If possible, how do I put constraints on the types of the function arguments? If not, how do I achieve the same result?
sort(>) is legal only when Element is Comparable. Since the element type of [T] is T, T must conform to Comparable in order for the array [T] to be sortable via >:
func backwards<T: Comparable>(array: [T]) -> [T] {
let reversedCollection = array.sort(>)
return reversedCollection
}

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