Swift protocol that is using an enum with generic associated type - swift

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.

Related

protocol annotation in var declaration breaks code

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

Swift 4 custom generic struct with optional array behaves strange?

I'm migrating a larger codebase to Swift 4. It includes a custom type that implements a matrix. In Swift 3.1 everything works well and as expected.
I was able to reduce the problem to two small code blocks. The first one defines the struct:
struct Matrix<Element> {
var array:[Element?] = []
mutating func setup(repeatedValue:Element) {
let fooTemp = Array<Element?>(repeating: repeatedValue, count: 10)
self.array = fooTemp
}
}
The problem itself comes when I add the Sequence implementation:
extension Matrix: Sequence {
typealias Iterator = AnyIterator<Element?>
func makeIterator() -> Iterator {
return AnyIterator(array.makeIterator())
}
}
As soon as the Sequence implementation is part of the code, I get a compiler error in the line self.array = fooTemp:
Cannot assign value of type '[Element??]' to type '[_?]'
I know, that I can fix this issue by using let fooTemp = Array<Element> in the code. The type inference does not respect this and make fooTemp an Array<Element?>.
What's going on here? Compiler shenanigans?
The issue occurs in Swift 3.2 as well.
The Sequence protocol defines
/// A type that provides the sequence's iteration interface and
/// encapsulates its iteration state.
associatedtype Element where Self.Element == Self.Iterator.Element
and that causes a conflict, since your Self.Iterator.Element is
Element?. Choosing a different name for your generic placeholder
solves the problem.

Difference between using Generic and Protocol as type parameters, what are the pros and cons of implement them in a function

Since Swift allows us using both Protocol and Generic as parameter types in a function, the scenario below has come into my mind:
protocol AProtocol {
var name: String{ get }
}
class ClassA: AProtocol {
var name = "Allen"
}
func printNameGeneric<T: AProtocol>(param: T) {
print(param.name)
}
func printNameProtocol(param: AProtocol) {
print(param.name)
}
The first function uses generic as parameter type with a type constraint, and the second function uses protocol as the parameter type directly. However, these two functions can have the same effect, which is the point confusing me. So my questions are:
What are the specific scenarios for each of them (or a case which can only be done by the specific one, but not another)?
For the given case, both functions turn out the same result. Which one is better to implement (or the pros and cons of each of them)?
This great talk has mentioned generic specialization, which is a optimization that turn the way of function dispatching from dynamic dispatching (function with non-generic parameters) to static dispatching or inlining (function with generic parameters). Since static dispatching and inlining are less expensive in contrast with dynamic dispatching, to implement functions with generic can always provide a better performance.
#Hamish also gave great information in this post, have a look for more information.
Here is a new question came to me:
struct StructA: AProtocol {
var a: Int
}
struct StructB: AProtocol {
var b: Int
}
func buttonClicked(sender: UIButton) {
var aVar: AProtocol
if sender == self.buttonA
{
aVar = StructA(a: 1)
}
else if sender == self.buttonA
{
aVar = StructB(b: 2)
}
foo(param: aVar)
}
func foo<T: AProtocol>(param: T) {
//do something
}
If there are several types conform to a Protocol, and are pass in to a generic function in different conditions dynamically. As shown above, pressing different buttons will pass different types(StructA or StructB) of parameter into function, would the generic specialization still work in this case?
There is actually a video from this year's WWDC about that (it was about performance of classes, structs and protocols; I don't have a link but you should be able to find it).
In your second function, where you pass a any value that conforms to that protocol, you are actually passing a container that has 24 bytes of storage for the passed value, and 16 bytes for type related information (to determine which methods to call, ergo dynamic dispatch). If the passed value is now bigger than 24 bytes in memory, the object will be allocated on the heap and the container stores a reference to that object! That is actually extremely time consuming and should certainly be avoided if possible.
In your first function, where you use a generic constraint, there is actually created another function by the compiler that explicitly performs the function's operations upon that type. (If you use this function with lots of different types, your code size may, however, increase significantly; see C++ code bloat for further reference.) However, the compiler can now statically dispatch the methods, inline the function if possible and does certainly not have to allocate any heap space. Stated in the video mentioned above, code size does not have to increase significantly as code can still be shared... so the function with generic constraint is certainly the way to go!
Now we have two protocol below:
protocol A {
func sometingA()
}
protocol B {
func sometingB()
}
Then the parameter need to conform to both A and B.
//Generic solution
func methodGeneric<T:A>(t:T)where T:B {
t.sometingA()
t.sometingB()
}
//we need protocol C to define the parameter type
protocol C:A,B {}
//Protocol solution
func methodProtocol(c:C){
c.sometingA()
c.sometingB()
}
It seems that nothing is wrong but when we define a struct like this:
struct S:A,B {
func sometingB() {
print("B")
}
func sometingA() {
print("A")
}
}
The methodGeneric works but we need to change struct S:A,B to struct S:C to make methodProtocol work. Some questions:
Do we really need protocol C?
Why not would we write like func method(s:S)?
You could read more about this in the Generic Doc for additional information .

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 Extensions for Collections

I'm working on a framework to make it easier to work with Key Value Observing and I've defined a protocol for converting native Swift types to NSObject as follows:
public protocol NSObjectConvertible {
func toNSObject () -> NSObject
}
Extending the builtin types was easy, simply defining the function to convert the given type to the appropriate NSObject:
extension Int8: NSObjectConvertible {
public func toNSObject () -> NSObject {
return NSNumber(char: self)
}
}
When I got to the Array type, I hit a number of snags, which I tried to work out. I didn't want to extend any array type, but only arrays whose element type was itself NSObjectConvertible. And naturally, needed Array to itself conform to the protocol.
After hunting around on SO, it looks like extending the Array type itself is a little harder because it's generic, but extending SequenceType can be done. Except that I can't both constrain the element type and declare its conformance to the protocol in the same declaration.
The following:
extension SequenceType where Generator.Element == NSObjectConvertible : NSObjectConvertible = {
public func toNSObject () -> NSObject {
return self.map() { return $0.toNSObject() }
}
}
Produces a compiler error:
Expected '{' in extension
And the carat points to the ":" where I'm trying to declare the protocol conformance. Removing the protocol conformance compiles without errors, but obviously doesn't help the case.
I'm not sure if this is a bug, or if Swift simply can't (or doesn't want to) support what I'm trying to do. Even if I simply define the extension, then try to take care of the conformance in the body, it produces the risk of passing sequences that don't really conform to what they should.
At best it's a hacky solution to just fail in cases where a sequence with non-conforming members are passed. I'd much rather let the compiler prevent it from happening.
(This is in Swift 2.1, Xcode 7.1.1)
You can't add the protocol conformance, unfortunately.