protocol annotation in var declaration breaks code - swift

I am trying to call the method .enumerate() on an instance of a type which conforms to the protocol Sequence. According to Apple's documentation, this should be correct, since .enumerate is part of the Sequence protocol.
However, I receive this complaint from the compiler:
Member 'enumerated' cannot be used on value of type 'any Sequence<URL>'; consider using a generic constraint instead.
Yet, if I remove the type annotation, then it works.
Here is an example which reproduces the problem:
func example() -> URL? {
let fm : FileManager = FileManager.default
let appDir : FileManager.SearchPathDirectory = FileManager.SearchPathDirectory.applicationDirectory
let domMask : FileManager.SearchPathDomainMask = FileManager.SearchPathDomainMask.allDomainsMask
let appResourceValues : [URLResourceKey] = [URLResourceKey.localizedNameKey]
var appURLs : any Sequence<URL> = fm.urls(for: appDir, in: domMask)
//var appURLs : Sequence<URL> = fm.urls(for: appDir, in: domMask)
//var appURLs = fm.urls(for: appDir, in: domMask)
var appURL : URL? = appURLs.enumerated().first { (offset: Int, element: URL) in
try! element.resourceValues(forKeys: Set(appResourceValues)).localizedName!.contains("App Store")
}?.element
return appURL
}
There are two commented lines in the code above, which are alternate ways to instantiate appURLs. If I use the first commented line, which is the old Swift syntax apparently, then I receive an error telling me that in order to add a type annotation which enforces a protocol, I need to use any protocolName, and not protocolName. (According to a comment on another post, this was a recent change in Swift: Use of protocol 'YourProtocol' as a type must be written 'any YourProtocol' Error, https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md)
If I use the second commented line, which removes the protocol annotation altogether, then the code works.
Is this a bug in Swift? How can I apply an annotation to indicate that it must conform to Sequence<URL> without breaking the code?
I tried to declare a generic type parameter, but Swift won't let me. None of these work:
associatedtype does exactly what I want: it creates a generic type parameter. But it doesn't work outside a protocol.

If you annotate appURLs with the existential type any Sequence<URL>, then that means that you don't know what concrete type it actually stores. This is problematic for calling enumerated, because enumerated returns EnumeratedSequence<Self>:
func enumerated() -> EnumeratedSequence<Self>
Self means "type on which this is called" - the exact thing that you don't know. Sometimes, having an unknown Self is fine. e.g. if methods with these signatures existed in Sequence:
func f() -> Self
func g() -> (Self, Int)
func h(p: (Self) -> Void)
func i() -> [Self]
func j() -> [Int: Self]
func k() -> Self?
All of these are covariant positions. It remains type safe to substitute Self in these positions with any Sequence<URL>, which is why you can still call these methods. However, it is not safe to do the same with EnumeratedSequence<Self>, because even though SomeConcreteImplementationOfSequence is a any Sequence<URL>, EnumeratedSequence<SomeConcreteImplementationOfSequence> is not a EnumeratedSequence<any Sequence<URL>>. Generics are invariant in Swift - the Self in EnumeratedSequence<Self> is in an invariant position.
You can see they talk about when functions involving Self can be called in this SE proposal:
[...] but references to Self-rooted associated types will for the
same reasons some Self references do today. As alluded to back in
Inconsistent Language Semantics, references to covariant Self
are already getting automatically replaced with the base object type,
permitting usage of Self-returning methods on existential values [...]
This way, a protocol or protocol extension member
(method/property/subscript/initializer) may be used on an existential
value unless:
The type of the invoked member (accessor — for storage declarations),
as viewed in context of the base type, contains references to Self
or Self-rooted associated types in non-covariant position. [...]
They even use enumerated() as an example further down!
extension Sequence {
public func enumerated() -> EnumeratedSequence<Self> {
return EnumeratedSequence(_base: self)
}
}
func printEnumerated(s: Sequence) {
// error: member 'enumerated' cannot be used on value of type protocol type 'Sequence'
// because it references 'Self' in invariant position; use a conformance constraint
// instead. [fix-it: printEnumerated(s: Sequence) -> printEnumerated<S: Sequence>(s: S)]
for (index, element) in s.enumerated() {
print("\(index) : \(element)")
}
}
Besides, EnumeratedSequence<any Sequence<URL>> isn't even a valid type! EnumeratedSequence requires its type parameter to be a Sequence, but any Sequence<URL> isn't one! Because Sequence has static requirements.
Responding to your comments,
It is bad practice to type hint something as [URL] when you only intend to use the qualities encapsulated by Sequence protocol
That is not bad practice. Rather, putting type annotations where they are not needed is considered not Swifty.
Example: I may want to use a method which compiles a enumerable list of URLs, but the way that these URLs are fetched will depend on runtime parameters (e.g., do I have internet access? Is an external drive currently mounted?). Depending on these parameters, it may be more efficient (or only be possible) to acquire the list of URLs as [URL] or as any other type which conforms to Sequence<URL>. In that case, the return type of such a function will be anything which conforms to Sequence<URL>
In that case, your function can return an AnySequence<URL>. Unlike any Sequence<URL>, this is a concrete type. You just need to do the extra step of wrapping other sequence types to it:
func fetchSomeURLs() -> AnySequence<URL> {
if someCondition {
return AnySequence([url1, url2]) // [URL]
} else {
return AnySequence(someOtherImplementationOfSequence)
}
}

I have concluded that the Swift compiler isn't sophisticated enough to check conformity of a variable to a protocol. (There are some limited cases where it will work.)
A work-around in this case is the following:
extension Sequence {
func enumerated_custom() -> any Sequence<(offset:Int, element:Iterator.Element)> {
var index : Int = -1
return self.lazy.map({ (el:Iterator.Element) in
index = index+1 ; return (index+1, el)
})
}
}
Then, we can do appURLs.enumerated_custom() to get a Sequence<(Int, URL)> which mimics the behaviour of appURLs.enumerated(). I can then use the annotation appURLs : Sequence<URL> in order to check for conformity, and the code does not break.
Related informational links:
https://belkadan.com/blog/2021/10/Swift-Regret-Generic-Parameters-are-not-Members/
https://github.com/apple/swift/pull/39492
https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md
https://github.com/apple/swift/pull/41131

Related

(type: T.Type) makes generic method work whereas without it T cannot be inferred

I'm using a simple method to run a fetch request on my Core Data DB. I'm housing most of my code within one protocol that will have default implementations that certain things will extend. My code is as follows:
protocol Modelable {
func testFetch<T>(type: T.Type) -> [T]?
}
extension Modelable {
func testFetch<T>(type: T.Type) -> [T]? {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Data"),
result = try? context.fetch(fetchRequest)
return result as? [T]
}
}
Then I simply call it on a struct that conforms to Modelable
let modelableExtended = ModelableExtended()
print(modelableExtended.testFetch(type: SomeType.self)) // Prints
What's weird about this code is that if I remove the type: T.Type from the method definition and where I call it, I'll get the following error:
Generic parameter 'T' could not be inferred
How exactly does passing a type into this method make it so it works? Isn't that just a parameter on the method itself, passing or not shouldn't change anything.
If you don’t want to pass in the type, you can do something like this instead
let modelableExtended = ModelableExtended()
let result: [SomeType]? = modelableExtended.testFetch()
print(result)
At some point you have to say what T is by either passing in some parameter that refers to it or by assigning the result.
Generics are a compile-time feature. Perhaps you are thinking that they are like Any or AnyObject where you never have to say the type and then you can find out at runtime what you actually have. This isn’t how generics work.
A generic let’s you write code that can work with a lot of different types, but the code that actually runs is using a specific type that was inferred from its context. If it cannot be inferred, then the code will not compile and never be run.

Can a protocol define subscript(keyPath:) without an explicit implementation in the adopting object?

Since Swift 4, objects have gained subscript(keyPath:) which can be used to retrieve values using AnyKeyPath and its subclasses. According to the Swift book, the subscript is available on all types. For example, an instance of a class TestClass may be subscripted with an AnyKeyPath like so:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
This compiles correctly as expected. Use of any other valid subclass would also compile including PartialKeyPath<TestClass>, KeyPath<TestClass, Bool>, etc. This functionality is unavailable in a protocol extension. For example, the following is invalid:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
If we want to use that keyPath subscript in the protocol, we can include it in the protocol definition. However, the compiler will not resolve it automatically:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
With this, we get a compile error: Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'. In order to resolve this, we must include a redundant implementation of that subscript in TestClass:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
This resolves the conformance issue and produces the goal result although it is seemingly unnecessary and illogical. I'm not sure how, but the subscript implementation is never even used. It's finding the expected implementation of subscript(keyPath:) and using that instead, but how? Where is that and is there any way to use it in a protocol? Why is this required by the compiler even though it's never used?
The context of this use case is in a logging module. The goal is that an object should be able to adopt a particular protocol which, with no additional setup on the object, would provide a human readable description of the object, instead of the default for many objects which is a memory address. The protocol would use Mirror to fetch KeyPaths of an object, read the values, and print them to the console. It is intended for debugging purposes and would not run in any production environment.
Please let me know if I can make any clarifications. I may post this to the Swift team if others think that this could potentially be a bug of sorts. All help is appreciated. Thanks in advance.
Full gist located here.

How Do I Instantiate AnyGenerator<T> in Swift?

So I'm trying to use AnyGenerator to wrap a generic GeneratorType, however I'm getting the error:
Argument passed to call that takes no argument
This seems to extend from a weird ambiguity as there is both an AnyGenerator struct (the one I expect) and an AnyGenerator class intended as an abstract class for implementations to extend.
However I don't have any idea how to specify one over the other, as all documentation I can find suggests I should just use:
let someGenerator = ["foo"].generate()
let anyGenerator = AnyGenerator(someGenerator)
Is there something else that I can do instead? For extra fun it seems there's also an anyGenerator global function, just to make sure no-one has any idea what's going on ;)
You can use the anyGenerator<G : GeneratorType>(_: G) function to create your AnyGenerator<...> instance
signature:
func anyGenerator<G : GeneratorType>(base: G) -> AnyGenerator<G.Element>
description:
Return a GeneratorType instance that wraps base but whose type depends only on the type of G.Element.
Example:
struct Foo : GeneratorType {
typealias Element = String
mutating func next() -> String? {
return "foo"
}
}
var bar = anyGenerator(Foo())
func foofoo(bar: AnyGenerator<String>) {
print("foobar")
}
foofoo(bar) // "foobar", ok
As I wrote in my comment to your question, you should probably avoid the specific name anyGenerator specifically since there exists a global native function anyGenerator(..).
Also, please see #MartinR:s comment to your question above; the two global anyGenerator functions are soon-to-be deprecated (Swift 2.2), and will be removed in Swift 3.0, in favour of initializers on AnyGenerator.

Constraining one generic with another in Swift

I've run into a problem where I have some protocol:
protocol Baz {
func bar<T>(input:T)
}
The function bar is made generic because I don't want the protocol itself to have a Self(it needs to be useable in a collection). I have an implementation of the protocol defined as:
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
value = input
}
}
This gives an error because the compiler doesn't know that S and T are the same type. Ideally I should be able to write something like:
func bar<T where T==S>(input:T) {
value = input
}
or
func bar<T:S>(input:T) {
value = input
}
The first form gives a "Same-type requirement makes generic parameter 'S' and 'T' equivalent" error (which is exactly what I'm trying to do, so not sure why it's an error). The second form gives me a "Inheritance from non-protocol, non-class type 'S'".
Any ideas of on either how to get this to work, or a better design pattern in Swift?
Update: As #luk2302 pointed out, I forgot to make Foo adhere to the Baz protocol
#luk2302 has hinted at much of this in the comments, but just to make it explicit for future searchers.
protocol Baz {
func bar<T>(input:T)
}
This protocol is almost certainly useless as written. It is effectively identical to the following protocol (which is also almost completely useless):
protocol Baz {
func bar(input:Any)
}
You very likely mean (and hint that you mean):
protocol Baz {
typealias T
func bar(input: T)
}
As you note, this makes the protocol a PAT (protocol with associated type), which means you cannot put it directly into a collection. As you note, the usual solution to that, if you really need a collection of them, is a type eraser. It would be nice if Swift would automatically write the eraser for you, which it likely will be able to do in the future, and would eliminate the problem. That said, though slightly tedious, writing type erasers is very straightforward.
Now while you cannot put a PAT directly into a collection, you can put a generically-constrained PAT into a collection. So as long as you wrap the collection into a type that constrains T, it's still no problem.
If these become complex, the constraint code can become tedious and very repetitive. This can be improved through a number of techniques, however.
Generic structs with static methods can be used to avoid repeatedly providing constraints on free-functions.
The protocol can be converted into a generic struct (this formalizes the type eraser as the primary type rather than "as needed").
Protocols can be replaced with functions in many cases. For example, given this:
protocol Bar {
typealias T
func bar(input: T)
}
struct Foo : Bar {
func bar(input: Int) {}
}
You can't do this:
let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements
But you can easily do this, which is just as good:
let bars = [(Int) -> Void] = [Foo().bar]
This is particularly powerful for single-method protocols.
A mix of protocols, generics, and functions is much more powerful than trying to force everything into the protocol box, at least until protocols add a few more missing features to fulfill their promise.
(It would be easier to give specific advice to a specific problem. There is no one answer that solves all issues.)
EDITED (Workaround for "... an error because the compiler doesn't know that S and T are the same type.")
First of all: This is just an separate note (and perhaps an attempt at redemption for my previous answer that ended up being myself chasing my own tail to compute lots and lots of redundant code) in addition to Robs excellent answer.
The following workaround will possibly let your implementation protocol Foo ... / class Bas : Foo ... mimic the behaviour you initially asked for, in the sense that class method bar(...) will know if generics S and T are actually the same type, while Foo still conforms to the protocol also in the case where S is not of the same type as T.
protocol Baz {
func bar<T>(input:T)
}
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
print(a.value) // 1.0
a.bar(2.0)
print(a.value) // 2.0
let myInt = 1
a.bar(myInt) // Incompatible types. Throw...
print(a.value) // 2.0
// perhaps not a loophole we indended
let myAny : Any = 3.0
a.bar(myAny)
print(a.value) // 3.0
The Any and AnyObject loophole here could be redeemed by creating a dummy type constraint that you extend all types (that you wish for to use the generics) to, however not extending Any and AnyObject.
protocol NotAnyType {}
extension Int : NotAnyType {}
extension Double : NotAnyType {}
extension Optional : NotAnyType {}
// ...
protocol Baz {
func bar<T: NotAnyType>(input:T)
}
class Foo<S: NotAnyType>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T: NotAnyType>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
// ...
// no longer a loophole
let myAny : Any = 3.0
a.bar(myAny) // compile time error
let myAnyObject : AnyObject = 3.0
a.bar(myAnyObject) // compile time error
This, however, excludes Any and AnyObject from the generic in full (not only for "loophole casting"), which is perhaps not a sought after behaviour.

Swift protocol that is using an enum with generic associated type

I'm trying to create a protocol that is using a generic enum in swift.
The compiler throws this error: Protocol can only be used as a generic constraint because it has associated type requirements
Short code snipped:
enum GenericEnum<T> {
case Unassociated
case Associated(T)
}
protocol AssociatedProtocol {
typealias AssociatedType
func foo() -> GenericEnum<AssociatedType>
}
let bar = [AssociatedProtocol]()
You can find a longer example here.
Does anybody know a solution to that issue?
Here’s the problem: imagine some subsequent lines of code.
// none of this will compile...
var bar = [AssociatedProtocol]()
bar.append(GenericEnum.Associated(1))
bar.append(GenericEnum.Associated("hello")
let foo = bar[0].foo()
What type is foo? Is it a GenericEnum<Int> or a GenericEnum<String>? Or neither?
This is especially a problem because enums, like structs, are “value types”. That means their size is determined by what they contain. Take the following code:
let x = GenericEnum.Associated(1)
sizeofValue(x) // 9 - 1 byte for the enum, 8 for the Int
let y = GenericEnum.Associated("hello")
sizeofValue(y) // 25 - 1 byte for the enum, 24 for the String
Protocols with associated types are only really there to constrain generic functions. So this would be fine:
func f<T: AssociatedProtocol>(values: [T]) {
var bar = [T]() // T is an instance of a specific
// AssociatedProtocol where T.AssociatedType
// is fixed to some specific type
}
but to use it stand-alone doesn’t make sense (at least with the current version 1.2 of Swift – new features might enable other things in the version).
If you need the protocol to be used polymorphically dynamically at runtime, you would need to ditch the typealias. Then instead it can be used as a fixed-size reference.