I don't usually pollute the thread with +1, but I'm a sucker for aesthetics and easy to read/understand code. Also, any feature that slims down the already verbose generic declarations is great news. So, +1. Thanks!
If v1.Type1 were independently a good idea with its own supporting use cases, this would fall out of that choice. However, I don't think that extending opaque parameter types in this direction is appropriate: if you need to refer to the same generic type parameter more than once in the declaration, give it a name.
Although some P⦠is technically not available, (some P)⦠should be accepted as <T: P>Tā¦. Thus I think the expression of an opaque type cannot be used in a variadic parameter is not precise and can be somehow revised.
Since this is a new feature, I think it would be prudent to disallow (some P)⦠in favor of generic parameter packs Variadic Generics and if Parameter Packs are not implemented then the old syntax can be expanded.
Personally, I interpret some P as āa single unspecified implementation of Pā.
If that interpretation is valid, this:
is unambiguously equivalent to this:
This doesnāt actually make any sense:
[_T1: _T2]... is fine, thatās a variable number of Dictionary instances. But what would the Key and Value be in one of those instances? A variable number of types conforming to Hashable and Equatable respectively? Impossible: Key and Value each need to be exactly one type. In fact, theyāre explicitly stated to be a single implementation using some!
The shorthand Iād expect to describe what I think you mean would be:
func f(_: any [some Hashable: some Equatable]...)
That reads as āa function that takes a variable number of instances of any type of Dictionary[1], where the Key is Hashable and the Value is Equatableā.
Iād like to see opaque types become much more pervasive throughout Swift. They allow a programmer to use a specific type while conveying what about that type they actually care about[2]. I think thatās profoundly useful for improving code readability, particularly when refactoring[3].
āDoes this algorithm actually need that local variable to be an Array, or just some Sequence?. If the latter, I could make this lazy to avoid unnecessary allocation.ā
let doubled: some Sequence = [1, 2, 3].map { $0 * 2}
There is a variable number of Dictionary instances, each of which can have different Key and Value types. For example, you can pass [Int: String] and [String: Double]. The Key types are stored in the type parameter pack _T1, and the Value types are stored in the type parameter pack _T2.
Both interpretations of opaque types in a variadic parameter type make sense. The variadic-generics version is more general, because it accepts a superset of inputs.
Which means that _T1 isnāt aKey, and _T2 isnāt aValue. Right?
The main thing throwing me off here is that the type is specified with a simple dictionary literal: [_T1: _T2]. When I look at a dictionary literal, I think āThis is one type[1]ā
typealias [some Hashable: some Equatable] = Dictionary<some Hashable, some Equatable>
To accept any dictionary that meets those conditions, wouldnāt you say just that?
any Dictionary<some Hashable, some Equatable>
Iām not sure I follow. Dictionary isnāt a concrete type: the associated types are part of the concrete type (and not the instance). At the language level, Dictionary<Int: String> and Dictionary<String: Double> are completely different types, connected only by sharing, for lack of a more accurate term, āconformanceā to Dictionary.
In other words, thereās a variable number of instances, each of which can have different Dictionary types. Thatād only be one variadic generic.
There are no opaque parameters here. The variadic generics proposal under discussion is clear about what this means. Let's form a call:
``swift
let intsToStrings: [Int: String] = [:]
let stringsToDoubles: [String: Double] = [:]
let intsToDoubles: [Int: Double] = [:]
f(intsToStrings, stringsToDoubles, intsToDoubles) // okay: T1 = { Int, String, Int }, T2 = {String, Double, Double}
As you can see, the arguments to `f` are all dictionaries, but instantiated with different key and value types. Because `T1` and `T2` are parameter packs (declared with the `...`), they represent multiple generic arguments, as seen in the lists above.
If you drop the `...` from either generic parameter, you get a different API. For example, we could require that all of the value types be the same:
```swift
func g<T1: Hashable..., T2: Equatable>(_ dicts: [T1: T2]...)
// ...
g(stringsToDoubles, intsToDoubles) // okay: T1 = { String, Int }, T2 = Double
g(intsToStrings, stringsToDoubles, intsToDoubles) // error: T1 = { Int, String, Int }, but T2 can't be both String and Double
Dictionary is a generic type, but it's not a protocol and there is no conformance to it. Dictionary<T, U> is a particular specialization of the generic type: for different T and U arguments it will create a distinct type, but they are all specializations of the same generic type and have the same structure. The structural similarity is hugely important to Swift's type-checking model for generics.
What is the story around diagnostics and error messages? Since this is partially about the learning curve, I think that how we would refer to each type in func horizontal(_ v1: some View, _ v2: some View, _ b1: some Button, _ b2: some Button ) -> some View I can't think of anything better than _SomeView1 and _SomeButton2 but think I want to explicitly come out against _T1 and the like if we can do better
The compiler will currently spell out the type as the opaque type, e.g., it will say some View. It'd also like it to highlight (e.g., in the editor) the source range where that some View occurs, to help with disambiguation. There's some affordance in the compiler already to give longer descriptions if there are two opaque types that look similar but aren't, e.g., the two some Views in the horizontal example could be distinguished by, e.g., some View (for parameter 'b1').
Ah, yes, this is requirement inference. It is supported, so the latter infers the Hashable requirement on the opaque type.
The first is that opaque parameter types can only be used in parameters of a function, initializer, or subscript declaration, and not in (e.g.) a typealias or any value of function type
I have one more question; is the next code possible? It's a parameter of function and also value of function type. Which restriction has priority?
It's a small thing, but in SE-0328, () -> (some P) is allowed. Therefore, I guess "any value of function argument type" or similar explanation is more appropriate in the quote.
Given the restrictions on using opaque types in argument position in function values and type aliases, presumably this code does not compile?
struct S {
static func staticFunc(_: some Any) { }
func instanceFunc(_: some Any) { }
}
type(of: S.staticFunc)
type(of: S.instanceFunc)
type(of: S().instanceFunc)
Where precisely does the failure come from? Does the language still have no way of representing these functionsā types, or does the failure come from some hard codes check that prevents constructing the argument to type(of:)?
The failure is that there is insufficient context to infer an argument for the implicit generic parameter. The proposal notes the issue by example (but it isn't explained -- I can fix that):
func f(_ p: some P) { }
let fAmbiguous = f // error: cannot infer argument for `some P` parameter
It's the same failure you would get if you'd declared f with a generic parameter, because the Swift type system doesn't have first-class generic functions, i.e., you cannot have a value of type <T>(T) -> Void: context must provide a specific binding for T.
No, the operand to some should be a protocol or protocol composition, not an existential type.
Might this feature be added in the future? If so, is this proposal forward-compatible with it? (I suspect it is, but it may not be able to express all types that a syntax with explicit type parameters could express).
I canāt recall whether func f<T: P>(any T) is legal. If it is, why shouldnāt func f(some any P) be legal? āBecause the first version is clearerā is a valid answer, but a bit of an odd one.