How to express the type of a constrained-generic function in Swift? - 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.

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.

Why may we use "internal argument labels" in type annotations of closures, when they (seemingly) can never be accessed?

Background
This is naturally legal:
let closure: (Int, Int) -> () = { print($0 + $1) }
closure(1, 2) // 3
Whereas, since the implementation of evolution proposal
SE-0111: Remove type system significance of function argument labels
in Swift 3, the following is not legal:
let closure: (a: Int, b: Int) -> () = { /* ... */ }
Error: function types may not have argument label a, use _ instead.
Which is expected, as, quoting from SE-0111:
Function types may only be defined in terms of the types of the formal
parameters and the return value.
Curiously, however (and as prompted by the error message above), this is legal:
let closure: (_ a: Int, _ b: Int) -> () = { print($0 + $1) }
closure(1, 2) // 3
However, as far as I can tell, we can't ever make use of a and b above (they shouldn't even be allowed, as they are not part of the types of the parameters?).
Question
Is there any reason for the final code snippet above to be legal? Can we make use or access the "internal argument labels" a and b (given the quote, we shouldn't ...), or is this possibly an oversight in the implementation of SE-0111?
What you're observing is the ability to define "purely cosmetic" parameter labels for closure types. This was accepted as a part of SE-0111, as stated in the rationale:
In response to community feedback, the core team is accepting the proposal with a revision to allow “purely cosmetic” parameter labels in closure types for documentation (as outlined in the alternatives section).
The syntax for these cosmetic parameter labels changed after the proposal to require an argument label of _, in order to make it explicit that the cosmetic labels aren't used at the call-site. This was detailed in an additional commentary:
The specific revision requested by the core team to SE-0111 is that
all “cosmetic” labels should be required to include an API name of _.
For example, this would not be allowed:
var op : (lhs : Int, rhs : Int) -> Int
instead, it should be spelled as:
var op : (_ lhs : Int, _ rhs : Int) -> Int
Although really, in practice, this makes the cosmetic labels fairly useless, as they don't show up at the call-site or even in auto-completion – only at the actual declaration itself. Therefore their intent to be self-documenting is somewhat lost.
The Swift team are aware of this shortcoming, and will be looking to make a purely additive change post-Swift 3 in order to rectify the situation.
Here's the sketch that they proposed (again, in the additional commentary):
First, we extend declaration names for variables, properties, and parameters to allow parameter names as part of their declaration name. For example:
var op(lhs:,rhs:) : (Int, Int) -> Int // variable or property.
x = op(lhs: 1, rhs: 2) // use of the variable or property.
// API name of parameter is “opToUse”, internal name is "op(lhs:,rhs:)”.
func foo(opToUse op(lhs:,rhs:) : (Int, Int) -> Int) {
x = op(lhs: 1, rhs: 2) // use of the parameter
}
foo(opToUse: +) // call of the function
This will restore the ability to express the idea of a closure
parameter that carries labels as part of its declaration, without
requiring parameter labels to be part of the type system (allowing,
e.g. the operator + to be passed into something that requires
parameter labels).
Second, extend the rules for function types to allow parameter API
labels if and only if they are used as the type of a declaration
that allows parameter labels, and interpret them as a sugar form for
providing those labels on the underlying declaration. This means that
the example above could be spelled as:
var op : (lhs: Int, rhs: Int) -> Int // Nice declaration syntax
x = op(lhs: 1, rhs: 2) // Same as above
// API name of parameter is “opToUse”, internal name is "op(lhs:,rhs:)”.
func foo(opToUse op : (lhs: Int, rhs: Int) -> Int) {
x = op(lhs: 1, rhs: 2) // Same as above.
}
foo(opToUse: +) // Same as above.
This proposed solution quite nicely allows the labels to be used at the call-site, allowing for self-documenting parameter labels, while not complicating the type-system with them. Additionally (in most cases) it allows for the expressive syntax of writing the labels next to the parameter types of the closure – which we were used to doing pre-Swift 3.
There have been complaints, not unreasonable, that if you remove the ability to name the parameters, you lose an important aspect of the human communication that tells a future maintainer or user of this code the purpose of these parameters. Well, that ability has been removed as far as external parameter names are concerned. But by leaving it open to sneak past the compiler by using just internal parameter names, we recover at least something of that communication.
However, we do not recover enough of that communication, because the internal parameters don't show up in code completion:
This has been criticized as a flaw in the new regime on this matter, and, in my opinion, rightly so.

Annotating the whole of a function declaration with a typealias

My goal is to use a typealias as a one-word reminder “attached” to a function declaration. Say,
typealias VoidToVoid = () -> ()
I can use the type alias when stating the expected type of a closure, thus
let f : VoidToVoid = { return }
assert(f is VoidToVoid)
However, this does not seem the most usual way of declaring functions. I was hoping for a way to tell the reader that a function declared like f′ below, should be of type VoidToVoid, and using that name.
(But without the reader having to infer, or to think, or to wait for the compiler to tell him or her about the type. Aggravating the situation, the compiler cannot know the type alias name I had wanted with f′ and will likely output messages stating the bare type, not the alias.)
func f′() : VoidToVoid { // tentative syntax
return
}
Can I get there at all?
Edit: There are two ends at which the type alias is to be used. At one end, this is now possible:
func giveMeAClosure(_ f: VoidToVoid) {
f()
}
At the other end, being VoidToVoid is implicit:
func canBeGivenAsClosure() {
// ...
}
That is, where canBeGivenAsClosure is declared, it is true but not obvious that there is a connection between the two ends through VoidToVoid. This is different from the let case, which can have "VoidToVoid" as its type annotation.
Closures are unnamed closure expressions, which is why we may use a function type typealias to specify the type of the closure, even for closures that have a non-empty argument list.
typealias VoidToVoid = () -> ()
typealias IntToVoid = (Int) -> ()
// unnamed closure expressions
let f: VoidToVoid = { print("foo") }
let g: IntToVoid = { print($0) }
Note here that f and g in these exaples are not functions: they are simply immutable properties that holds references to (the backing storage of) the unnamed closures specified at the time of their declaration.
A function, one the other hand, is a special case of a closure; a closure with a name. Moreover, any function with a non-empty argument list needs to supply in internal (and optionally) and external parameter names in its declaration. A function type typealias, however, may not contain argument labels for its parameter names:
typealias IntToVoid = (a: Int) -> ()
/* Error: function types cannot have argument
label 'a'; use '_' instead */
This means that a function type typealias can't possible be used a substitute for the combined parameter and return type declaration part of a function (not even in the () -> () case).
For details, see e.g.:
The Language Guide - Closures
Closures
...
Global and nested functions, as introduced in Functions, are actually
special cases of closures. Closures take one of three forms:
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
On another note, you may naturally test the type of a given function to some existing function type typealias.
typealias VoidToVoid = () -> ()
typealias IntToVoid = (Int) -> ()
func f() -> () {}
func g(_ a: Int) -> () { _ = a }
print(f is VoidToVoid) // true
print(g is IntToVoid) // true

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
}

Closure with generic parameters

I'm having trouble writing the following function as a closure
func myfunc<S where S: MyProtocol, S: MySuperClass>(param: S) { ... }
I tried
let myClosure = {<S where S: MyProtocol, S: MySuperClass>(param: S) in ... }
, but it doesn't work.
Any suggestions of how I can accomplish this?
I believe what you're asking for can't make sense (having nothing to do with Swift). While I'm interested in being proven wrong, I don't believe this could be reasonably created in any strongly typed language. (EDIT: continuing my research, I believe this would be possible in a language with first-class polymorphism, but I am not aware of any general-use languages that actually have this feature.)
let myClosure = {<S where S: MyProtocol, S: MySuperClass>(param: S) in ... }
What type would you expect myClosure to be? A generic creates an abstract type. It does not become a real type until it is specialized. So myClosure would be of an abstract type itself. That's like asking for an instance of an abstract class. The whole point of "abstract" is you can't construct one. The best you could say would be that myClosure would itself be a type that you would need to instantiate into a real instance (but then let doesn't make any sense; you don't let types).
When you wrap this in a struct, what you're really doing is creating an abstract type that you will specialize into a real type when you create an instance.
Now what would make sense IMO (but appears currently to be impossible), is this:
typealias Mapping<S> = S -> S
let identity: Mapping<Int> = { return $0 }
That makes sense because you're defining an abstract type (Mapping), but then instantiating a concrete type Mapping<Int>. Unfortunately, typealias does not appear to support generics at this point, so a struct is probably the best tool we have.
Note that while typealias is a bust, it is clearly possible to specialize function variables themselves. This isn't a closure, I know, but may be useful in some of the same situations.
func Identity<T>(i:T) -> T {
return i
}
let identityInt:(Int -> Int) = Identity
identityInt(1) // => 1
Using this to explore the problem of abstract types a little more, consider:
func Identity<T>(i:T) -> T { return i }
let x = Identity
This fails to compile with the error:
error: cannot convert the expression's type '(T) -> T' to type '(T) -> T'
That's because the type (T) -> T is not a concrete type, so you can't have one called x. Compare that to identityInt, which I explicitly specialized into a concrete type, and then could construct.
You could try wrapping your closure within a struct that declares the generic type. Something like:
struct ClosureWrapper<S where S: MyClass, S: MyProtocol> {
let myClosure = {(param: S) in ... }
}
Of course, at this point your closure may as well be a normal function :P