How can I restore type generic type inference in Swift result builders? - swift

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.

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.

Generic parameter 'T' could not be inferred after assignment

// Xcode 11.6 / Swift 5
import Foundation
func f<T>(_: (T) -> Void) { }
#discardableResult
func g(_: Int) -> Int { 0 }
f { g($0) } // compiles fine
f { let _ = g($0) } // Generic parameter 'T' could not be inferred
In the above code, the generic function f expects as its argument a function that takes an argument of type T.
The function g takes an argument of type Int.
When I write f { g($0) }, the code compiles. I believe (please correct me if I'm wrong) this compiles because the compiler can infer that T is an Int based on g's argument type.
However, when I try to do something with the return value of g, for example in the let _ = g($0) line, the compiler complains that it can no longer infer the type of T.
It seems to me the return type of g should have no bearing on how the compiler infers T's type, but clearly it does.
Can anyone shed some light on why this happens, and what (if anything) can be done to correct it?
This may or may not be a compiler bug.
It is known that Swift does not try to infer the types of some closures, namely, multi-statement ones, as said in SR-1570:
This is correct behavior: Swift does not infer parameter or return types from the bodies of multi-statement closures.
However, your closure consists of only one statement, one declaration to be specific. It is possible, albeit weird, that they designed it so that Swift doesn't try to infer types if the closure contains one declaration as well. For example, this does not compile either
f { let x: Int = $0 } // nothing to do with "g"! The issue seems to be with declarations
If this were by-design, the rationale behind it might be because a single declaration in a closure doesn't make much sense anyway. Whatever is declared, won't be used.
But again, this is just speculation, and this could be a bug as well.
To fix it, simply make it a not-a-declaration:
f { _ = g($0) } // this, without the "let", is IMO the idiomatic way to ignore the function result
Or
f { g($0) } // g has #discardableResult anyway, so you don't even need the wildcard
The function f takes in a function as a parameter which in turn takes a parameter of type T and returns nothing (Void). For a closure to infer types automatically, it has to consist of single (and sometimes simple) expression. Anything complex makes it difficult for the compiler to infer (which makes sense from the compiler's standpoint). Apparently, let _ = g($0) is a complex statement as far as the compiler is concerned. For further information, see this mailing list discussion
it looks like #discardableResult gives you an ability to have 2 types of functions:
g(_: Int) -> Int and g(_: Int) -> Void (it is when you don't want to use a result of function)
I think that
f { g($0) } - here your f can infer a type because it has the same Type
(_: (T) -> Void) and (_: (Int) -> Void)
f { let _ = g($0) } - in this case the type of g function is different from f function
(_: (T) -> Void) and (_: (Int) -> Int)
If you will remove "let" it will compile again:
f { _ = g($0) }
I think that
f { let _ = g($0) } - return only Int value
f { _ = g($0) } - return function (_: (Int) -> Int)
Maybe it is a key here

cast generic type to the type with Any as the type parameter [duplicate]

I'm using Signals library.
Let's say I defined BaseProtocol protocol and ChildClass which conforms BaseProtocol.
protocol BaseProtocol {}
class ChildClass: BaseProtocol {}
Now I want to store signals like:
var signals: Array<Signal<BaseProtocol>> = []
let signalOfChild = Signal<ChildClass>()
signals.append(signalOfChild)
I get error:
But I can write next lines without any compiler error:
var arrays = Array<Array<BaseProtocol>>()
let arrayOfChild = Array<ChildClass>()
arrays.append(arrayOfChild)
So, what the difference between generic Swift Array and generic Signal?
The difference is that Array (and Set and Dictionary) get special treatment from the compiler, allowing for covariance (I go into this in slightly more detail in this Q&A).
However arbitrary generic types are invariant, meaning that X<T> is a completely unrelated type to X<U> if T != U – any other typing relation between T and U (such as subtyping) is irrelevant. Applied to your case, Signal<ChildClass> and Signal<BaseProtocol> are unrelated types, even though ChildClass is a subtype of BaseProtocol (see also this Q&A).
One reason for this is it would completely break generic reference types that define contravariant things (such as function parameters and property setters) with respect to T.
For example, if you had implemented Signal as:
class Signal<T> {
var t: T
init(t: T) {
self.t = t
}
}
If you were able to say:
let signalInt = Signal(t: 5)
let signalAny: Signal<Any> = signalInt
you could then say:
signalAny.t = "wassup" // assigning a String to a Signal<Int>'s `t` property.
which is completely wrong, as you cannot assign a String to an Int property.
The reason why this kind of thing is safe for Array is that it's a value type – thus when you do:
let intArray = [2, 3, 4]
var anyArray : [Any] = intArray
anyArray.append("wassup")
there are no problems, as anyArray is a copy of intArray – thus the contravariance of append(_:) is not a problem.
However, this cannot be applied to arbitrary generic value types, as value types can contain any number of generic reference types, which leads us back down the dangerous road of allowing an illegal operation for generic reference types that define contravariant things.
As Rob says in his answer, the solution for reference types, if you need to maintain a reference to the same underlying instance, is to use a type-eraser.
If we consider the example:
protocol BaseProtocol {}
class ChildClass: BaseProtocol {}
class AnotherChild : BaseProtocol {}
class Signal<T> {
var t: T
init(t: T) {
self.t = t
}
}
let childSignal = Signal(t: ChildClass())
let anotherSignal = Signal(t: AnotherChild())
A type-eraser that wraps any Signal<T> instance where T conforms to BaseProtocol could look like this:
struct AnyBaseProtocolSignal {
private let _t: () -> BaseProtocol
var t: BaseProtocol { return _t() }
init<T : BaseProtocol>(_ base: Signal<T>) {
_t = { base.t }
}
}
// ...
let signals = [AnyBaseProtocolSignal(childSignal), AnyBaseProtocolSignal(anotherSignal)]
This now lets us talk in terms of heterogenous types of Signal where the T is some type that conforms to BaseProtocol.
However one problem with this wrapper is that we're restricted to talking in terms of BaseProtocol. What if we had AnotherProtocol and wanted a type-eraser for Signal instances where T conforms to AnotherProtocol?
One solution to this is to pass a transform function to the type-eraser, allowing us to perform an arbitrary upcast.
struct AnySignal<T> {
private let _t: () -> T
var t: T { return _t() }
init<U>(_ base: Signal<U>, transform: #escaping (U) -> T) {
_t = { transform(base.t) }
}
}
Now we can talk in terms of heterogenous types of Signal where T is some type that's convertible to some U, which is specified at the creation of the type-eraser.
let signals: [AnySignal<BaseProtocol>] = [
AnySignal(childSignal, transform: { $0 }),
AnySignal(anotherSignal, transform: { $0 })
// or AnySignal(childSignal, transform: { $0 as BaseProtocol })
// to be explicit.
]
However, the passing of the same transform function to each initialiser is a little unwieldy.
In Swift 3.1 (available with Xcode 8.3 beta), you can lift this burden from the caller by defining your own initialiser specifically for BaseProtocol in an extension:
extension AnySignal where T == BaseProtocol {
init<U : BaseProtocol>(_ base: Signal<U>) {
self.init(base, transform: { $0 })
}
}
(and repeat for any other protocol types you want to convert to)
Now you can just say:
let signals: [AnySignal<BaseProtocol>] = [
AnySignal(childSignal),
AnySignal(anotherSignal)
]
(You can actually remove the explicit type annotation for the array here, and the compiler will infer it to be [AnySignal<BaseProtocol>] – but if you're going to allow for more convenience initialisers, I would keep it explicit)
The solution for value types, or reference types where you want to specifically create a new instance, to is perform a conversion from Signal<T> (where T conforms to BaseProtocol) to Signal<BaseProtocol>.
In Swift 3.1, you can do this by defining a (convenience) initialiser in an extension for Signal types where T == BaseProtocol:
extension Signal where T == BaseProtocol {
convenience init<T : BaseProtocol>(other: Signal<T>) {
self.init(t: other.t)
}
}
// ...
let signals: [Signal<BaseProtocol>] = [
Signal(other: childSignal),
Signal(other: anotherSignal)
]
Pre Swift 3.1, this can be achieved with an instance method:
extension Signal where T : BaseProtocol {
func asBaseProtocol() -> Signal<BaseProtocol> {
return Signal<BaseProtocol>(t: t)
}
}
// ...
let signals: [Signal<BaseProtocol>] = [
childSignal.asBaseProtocol(),
anotherSignal.asBaseProtocol()
]
The procedure in both cases would be similar for a struct.

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

Swift 2.0 Generics and Type Safety Issues

I was doing this tutorial http://blog.teamtreehouse.com/introduction-learn-power-swift-generics and I came upon this code;
func someFunction<T, U>(a: T, b: U) {}
The problem is when I call the function using
someFunction<String, Int>(1, "Test")
I get an error saying "cannot explicitly specialize a generic function".
I then change it to
someFunction(1,b: "Test")
and now there is no error. The problem is that there is now no type safety. (Is there something wrong with the code, as it was written before swift 2.0?) What is the best way to re-introduce type safety?
The declaration is completely generic and is specifying that any two types can be used.
func someFunction<T, U>(a: T, b: U) {}
It is not that there is no type safety in Swift, this is how you express a generic without any type constraints.
You get what you ask for.
If you wanted to constrain the function to String and Int, you would have written it as
func someFunction(a:String, b:Int)
Generics are more often used with collections, protocols and classes. Basic types rarely need them :
func someFunction<T:Comparable, U:Comparable>(a:T, b:U) -> Bool
{ return (a < b) || (a > b) }
ok, see this 'self explanatory' example. try it in playground and play with it a little bit.
func foo<T>(t: T)->T {
return t
}
// here the resulting type is infered by compiler
let i = foo(1) // 1
let j: Int = foo(1) // 1
let t = foo("alpha") // "alpha"
// if you declare it self ..
//let s: String = foo(1) // error: cannot convert value of type 'Int' to expected argument type 'String'
/* this fails to compile!
func bar<T>(t:T)->Int {
return t.count
}
*/
/* this fails to compile too !!
func bar0<T:CollectionType>(t:T)->Int {
return t.count
}
*/
func bar<T:CollectionType>(t:T)->T.Index.Distance {
return t.count
}
let arr = [1,2,3]
let k:Int = bar(arr) // 3
print(k.dynamicType) // Int
// and even more interesting
let l = bar(arr)
print(l.dynamicType) // Int