I have a protocol with an associated type:
protocol MyProtocol {
associatedtype Q
}
Now I'd like to have an enum like
enum MyEnum<Q> {
case zero
case one(MyProtocol)
case two(MyProtocol, MyProtocol)
}
where each associated value has Q as its associated type. This doesn't work:
enum MyEnum<Q> {
case zero
case one<P: MyProtocol where P.Q == Q>(P)
case two<P1: MyProtocol, P2: MyProtocol where P1.Q == Q, P2.Q == Q>(P1, P2)
}
Apparently, individual enum members can't have their own generic constraints.
The only thing I can think of is to move those constraints to the enum declaration, but this fixates the associated types. To demonstrate why that's not what I want, this is what I'd like to be able to do:
struct StructA: MyProtocol {
typealias Q = Int
}
struct StructB: MyProtocol {
typealias Q = Int
}
var enumValue = MyEnum.one(StructA())
enumValue = .two(StructB(), StructA())
enumValue = .two(StructA(), StructB())
Is there a way around this limitation?
Type erasure. The answer is always type erasure.
What you need is an AnyProtocol type:
struct AnyProtocol<Element>: MyProtocol {
typealias Q = Element
// and the rest of the type-erasure forwarding, based on actual protocol
}
Now you can create an enum that uses them
enum MyEnum<Q> {
case zero
case one(AnyProtocol<Q>)
case two(AnyProtocol<Q>, AnyProtocol<Q>)
}
For a deeper discussion of how to build the type erasers see A Little Respect for AnySequence.
Swift cannot discuss PATs (protocols with associated types) as real types or even abstract types. They can only be constraints. In order to use it as even an abstract type, you have to distill it into a type eraser. Luckily this is quite mechanical and in most cases not difficult. It's so mechanical that eventually the compiler will hopefully do the work for you. But someone has to build the box, and today that's you.
Related
I've read the documentation, and seen perhaps only a part of what the protocol is. I just am not following the logic. Can someone help me understand this?
What I see in xcode when I examine the protocol
/// Conforming to the CaseIterable Protocol
/// =======================================
///
/// The compiler can automatically provide an implementation of the
/// `CaseIterable` requirements for any enumeration without associated values
/// or `#available` attributes on its cases. The synthesized `allCases`
/// collection provides the cases in order of their declaration.
///
/// You can take advantage of this compiler support when defining your own
/// custom enumeration by declaring conformance to `CaseIterable` in the
/// enumeration's original declaration. The `CompassDirection` example above
/// demonstrates this automatic implementation.
public protocol CaseIterable {
/// A type that can represent a collection of all values of this type.
associatedtype AllCases : Collection = [Self] where Self == Self.AllCases.Element
/// A collection of all values of this type.
static var allCases: Self.AllCases { get }
}
I'm struggling to follow what is happening here and why. Can someone walk me through the logic of this please?
One of the other big struggles I'm having because of this is if I conform a protocol to be CaseIterable.
protocol Foo: CaseIterable {}
I can't use it as a variable anymore.
struct Bar {
var foo: Foo
}
I get this error
Protocol 'Foo' can only be used as a generic constraint because it has Self or associated type requirements.
It does have Self requirements but I can't figure out how to get around this problem. If someone could help me understand why this happens and how to fix it too, I'd be very grateful.
Edit: - This is the playground code copied directly. I've updated it to use the some, but I'm not quite sure how to proceed past this error.
import Foundation
protocol Zot: CaseIterable {
var prop: Data { get }
}
enum Bar: Zot {
case thing3
case thing4
var prop: Data {
switch self {
case .thing3, .thing4: return Data()
}
}
init() {}
}
enum Baz: Zot {
case thing1
case thing2
var prop: Data {
switch self {
case .thing1, .thing2: return Data()
}
}
init() {}
}
enum Foo {
case bar
case baz
var otherValues: some Zot {
switch self {
case .bar:
return Bar
case .baz:
return Baz
}
}
}
CaseIterable exists to allow you to programmatically walk through all the possible cases of an enum, allowing you use the enum type as a Collection of its cases:
enum CardinalDirection: CaseIterable { case north, south, east, west }
for direction in CardinalDirection.allCases {
// Do something with direction which is one of north, south, east, west
print("\(direction)")
}
This prints
north
south
east
west
There is nothing that prevents you from making other kinds of types conform to CaseIterable; however, the compiler will only synthesize conformance for enum types. It's not useful for most other kinds of types; however, I have occasionally found it useful for types that conform to OptionSet. In that case you have to manually implement conformance.
struct AssetFlags: OptionSet, CaseIterable
{
typealias RawValue = UInt8
typealias AllCases = [AssetFlags]
let rawValue: RawValue
static let shouldPreload = AssetFlags(rawValue: 0x01)
static let isPurgeable = AssetFlags(rawValue: 0x02)
static let isLocked = AssetFlags(rawValue: 0x04)
static let isCached = AssetFlags(rawValue: 0x08)
static var allCases: AllCases = [shouldPreload, isPurgeable, isLocked, isCached]
}
Note that OptionSet is conceptually similar to an enum. They both define a small set of distinct values they can have. With one they are mutually exclusive, while for the other they may be combined. But the key thing for CaseIterable to be useful is the finite nature of the set of possible distinct values. If your type has that characteristic, conforming to CaseIterable could be useful, otherwise, it wouldn't make sense. Int or String, for example, are not good candidates for CaseIterable.
In my own code, I don't bother conforming to CaseIterable, even for enum types, until a specific need arises that requires it. Actually I take that approach to all protocol conformance. It's a specific case of the more general YAGNI rule of thumb: "You ain't gonna need it."
Regarding your Bar struct, the problem is not specifically related to CaseIterable, but rather to using a protocol with Self or associated type requirements, which for Foo happens to be inherited from CaseIterable.
Swift 5.7 relaxed the rules concerning Self and associated type requirements a bit, allowing you to use the any keyword to tell the compiler you want to use an existential Foo instead of a concrete Foo to write
struct Bar {
var foo: any Foo
}
If you want a concrete Foo you could use some. The original way to do it though, which still works, is to make Bar explicitly generic
struct Bar<T: Foo> {
var foo: T
}
Update based on revised question code
The way you're using enums is... well, let's say it's out of the ordinary. There are two problems. The first is that you're returning types not values:
enum Foo {
case bar
case baz
// Will return a *value* of a type that conforms to Zot
var otherValues: some Zot
{
switch self {
case .bar:
return Bar // Bar is a *type* not a value
case .baz:
return Baz // Baz is a *type* not a value
}
}
}
I'll fix this is in a way that is almost certainly wrong for what you want to do, but allows moving forward to the other problem. We need to return values, so I'll just pick the first of the corresponding cases of Bar and Baz, and that will expose the other problem.
enum Foo {
case bar
case baz
var otherValues: some Zot
{
switch self {
case .bar: return Bar.thing3
case .baz: return Baz.thing1
}
}
}
The problem here is that some means that there will be one specific concrete type that conforms to Zot, so the compiler will be able to access its properties and methods directly rather than via its protocol witness table... it's basically a way to have the efficiency of having the calling code use the concrete types without having to tie to calling code to the concrete type at the source code level. otherValues, however, returns a value of either of two types, so the return type would have to be an existential type rather than a concrete one. You could do this if you return any Zot instead of some Zot.
Of course even using any, this version is wrong, because it doesn't take into account half of the cases of Bar and Baz. I assume that you want to be able to construct a Foo from a Bar or Baz while preserving its original value somehow, and I guess retrieve it later.
Before I present solutions, I want to mention that without knowing exactly what you are trying to accomplish, your code feels like it took a wrong design turn at some point. It would probably be better to rethink how you're doing what you want to do to see if there is a better way.
If I understand what your trying to do, I can think of at least three of ways, none of which requires CaseIterable, but maybe that's needed for other reasons.
Option 1
The first is to define Foo so that it explicitly contains all of the cases of Bar and Baz:
enum Foo: Zot
{
case thing1, thing2, thing3, thing4
init(_ value: Bar)
{
switch value
{
case .thing3: self = .thing3
case .thing4: self = .thing4
}
}
init(_ value: Baz)
{
switch value
{
case .thing1: self = .thing1
case .thing2: self = .thing2
}
}
var prop: Data
{
switch self
{
case .thing1: return Baz.thing1.prop
case .thing2: return Baz.thing2.prop
case .thing3: return Bar.thing3.prop
case .thing4: return Bar.thing4.prop
}
}
var bazValue: Baz?
{
switch self
{
case .thing1: return .thing1
case .thing2: return .thing2
default: return nil
}
}
var barValue: Bar?
{
switch self
{
case .thing3: return .thing3
case .thing4: return .thing4
default: return nil
}
}
}
This has the advantage of being straight-forward, but will require more maintenance if you add/remove cases from Bar or Baz - or even add a whole other enum that conforms to Zot.
Option 2
The second way is to define Foo so that it uses associated values:
enum Foo
{
case bar(value: Bar)
case baz(value: Baz)
init(_ value: Bar) { self = .bar(value: value) }
init(_ value: Baz) { self = .baz(value: value) }
}
I think this second case is cleaner, and you don't need otherValues because usage code can do:
switch foo
{
case let .bar(value: bar):
// do whatever with bar
case let .baz(value: baz):
// do whatever with baz
}
Still assuming second version of Foo, maybe an even cleaner way is:
extension Foo
{
func withZotValue<R>(_ code: (any Zot) throws -> R) rethrows -> R
{
switch self
{
case let .bar(value: value): return try code(value)
case let .baz(value: value): return try code(value)
}
}
}
That allows you to eliminate a lot of switch statements in usage code. To use it:
foo.withZotValue { zotValue in
// Do something with zotValue that the Zot protocol supports.
}
If you need this second version of Foo to conform to CaseIterable, Swift won't synthesize conformance for you because of the associated values, but you can write the conformance yourself.
extension Foo: CaseIterable
{
typealias AllCases = [Self]
static var allCases: AllCases {
[
Baz.allCases.map { .baz(value: $0) },
Bar.allCases.map { .bar(value: $0) },
].joined()
}
}
Option 3:
The last possible solution, which actually should probably be the first, if it applies, would be to define whatever you're trying to do that's common to both Bar and Baz in Zot. Whether that's a good idea or not depends on what you're trying to do, but let's assume it does make sense. For example, I notice that both Bar and Baz support a prop property, but the Zot protocol doesn't list prop. Why not? Is it unrelated to "Zotness"? If anything that conforms to Zot should have a prop property, then add it to the protocol:
protocol Zot: CaseIterable {
var prop: Data { get }
}
You still provide implementations of prop in Bar and Baz - that's kind of like overriding a base class method in subclasses.
Let's say the Data returned from prop is encoded JSON and you want be able to decode a Codable thing from a Zot. You can now do that without caring if its a Bar or a Baz (or any other new Zot-conforming type you might add later):
func decode<T: Codable>(_ type: T.Type, from src: some Zot) throws -> T {
return try JSONDecoder().decode(T.self, from: src.prop)
}
I am attempting (for essentially no reason) to make a protocol which describes categories from category theory. I have tried to come up with something like this.
protocol Category {
associatedtype Object: Protocol
}
protocol Hom {
associatedtype C: Category
associatedtype Source: C.Object
associatedtype Target: C.Object
}
In particular, I want each Hom type to have an associated category C and an associated Source and Target type which are both objects in that category. Consequently, I have associated an Object protocol to each Category, and have tried to make the Source and Target of a Hom conform to the Object protocol for the corresponding Category. The code above fails to compile with
Type 'Self.Source' constrained to non-protocol, non-class type 'Self.C.Object'
This error is at least unclear, for C.Object is declared as a protocol. Is there any way I can work around this issue?
Edit:
As Rob has pointed out, the code as is does not make much sense. Protocol is a particular class from ObjC and is not a type which describes protocols. Further, there is no type that describes all protocols, because protocols themselves cannot conform to protocols as they are just requirements on other types. What I was looking for is a metatype which Any.Protocol, Sequence.Protocol, etc. were all instances of.
I will go into more detail as to what kind of construction I am trying to describe.
A category is a type Object and a type of homomorphisms between every pair of instances of Object. For two instances of Object, A and B, the type of homomorphisms is generally written as Hom(A,B), but I will write Hom<A,B> to be Swiftier. Categories then come equipped with composition which has the signature <A: Object, B: Object, C: Object>(_ f: Hom<A,B>, _ g: Hom<B,C>) -> Hom<A,C>.
If f is an instance of Hom<A,B>, then A is called the source or domain of f and B is called the target or codomain of f.
Types themselves are then a category where Object is the metatype of all types and Hom<A,B> = (A) -> B.
The primary reason categories are difficult in Swift is because Swift does not have dependent types. There is no way to describe a category whose object type is Int because there is no way to have a type Hom<0,0>. However, if the Object type is required to be a metatype, then suddenly Hom<A,B> is a sensical thing to describe to the type system because an instance of a metatype is a type (I think), which can be a generic parameter. This is what I attempted to describe by setting Object: Protocol.
In Swift, I would really wish to describe
protocol Category {
associatedtype Object: Metatype
associatedtype Hom<A: Object, B: Object>
func compose<A: Object, B: Object, C: Object>(_ f: Hom<A,B>, then g: Hom<B,C>) -> Hom<A,C>
}
but this is also a non-starter because associated types cannot have generic parameters.
In my use case, I have a protocol that describes finitely generated abelian groups and a protocol that describes finitely generated unital rings, and I would love to write generic code which does not care whether or not it is working with GroupHom<A,B> where A: AbelianGroup, B: Abelian Group, RingHom<A,B> where A: Ring, B: Ring, or (A) -> B as each of these come equipped with the correct kind of composition.
It might just be impossible to do this, which I would be willing to accept. Please let me know if this is different enough that it should be asked as a separate question.
associatedtype Object: Protocol
This line doesn't mean what you think it means. Protocol isn't part of Swift. It's part of the ObjC runtime. (It's a really confusing import.)
But even if it did mean that, I don't believe it would help you. It is important to realize that in Swift, protocols do not conform to protocols, and protocols with associated types are not types. They're constraints on types. This tends to creep in all over the place and surprise people.
My suspicion is that you're looking to model things along these lines (note that my category theory is very weak, so please forgive me if I use the wrong terms at points).
We want to get to a point where we can say "a category has a collection of objects and a collection of arrows between those objects." To get there, I think we'd want to start with a generic Arrow:
protocol Arrow {
associatedtype Source
associatedtype Target
var apply: (Source) -> Target { get }
}
A homomorphism is an arrow that maps back to its own type.
protocol Homomorphism: Arrow where Source == Target {
typealias Object = Source
}
And with that, we can express a Category:
protocol Category {
associatedtype Object
associatedtype Arrow: Homomorphism where Arrow.Object == Object
}
I'd like to talk about the category of integers and functions (I believe that's a proper category). So first I need functions.
struct Function<Source, Target>: Arrow {
let apply: (Source) -> Target
}
extension Function: Homomorphism where Source == Target {}
And then I can declare the category.
struct Integers: Category {
typealias Object = Int
typealias Arrow = Function<Int, Int>
}
And create a morphism.
let increment = Function(apply: { (x: Int) in x + 1 })
I think this is somewhat the direction you're looking for.
dg
In swift you cannot use a protocol associatedtype as an associatedType cause it is not defined what type is.
Associated Type can only be used as a constraint for types, like this:
protocol Category {
associatedtype Object1:Equatable
}
class Homs:Category{
typealias Object1 = Int
func sum(element:Object1){
print(element+element)
}
}
Or like this
protocol Category {
associatedtype Object1: Equatable
}
protocol Homs {
associatedtype Cat:Category
associatedtype Source: Category where Source.Object1 == Cat.Object1
associatedtype Target: Category where Target.Object1 == Cat.Object1
}
The reason of why your code not compile, is cause, your associatedtype Target is constrained to implements a associatedType (that is not defined) of a protocol. Someone need to define it before to use as a constraint.
An approach to resolve your problem clould be, generate a generic class. Let's to see it:
protocol Category {
associatedtype Object1: Equatable
}
class Homs<Cat:Category,Source,Target> where Cat.Object1 == Source && Cat.Object1 == Target.Object1{
}
Another approach could be create a generic Category class, and a protocol with type for Category class, category Class, Source and Target conforming to Type:
class Category<T>{
}
protocol Homs {
associatedtype ObjectType
associatedtype Cat:Category<ObjectType>
associatedtype Source where Source == ObjectType
associatedtype Target where Target == ObjectType
}
O something like the second example:
protocol Category {
associatedtype Object1: Equatable
}
protocol Homs {
associatedtype Cat:Category
associatedtype Source: Category where Source.Object1 == Cat.Object1
associatedtype Target: Category where Target.Object1 == Cat.Object1
}
Remember, You cannot use protocols with associated types as a type constraint for associated type or as a type for a variable or constant, first someone need to define the associated types.
I hope I helped you.
The following code (compiles without errors) retrieves index of an element
in a particular CaseIterable enum type
public enum MyEnum : CaseIterable {
case ONE, TWO, THREE
public func ordinal() -> Int? {
return MyEnum.allCases.firstIndex(of: self)
}
}
I want to make a generic function to work with all CaseIterable enums.
If I try:
public extension CaseIterable {
public func ordinal() -> Int? {
return CaseIterable.allCases.firstIndex(of: self)
}
}
I get a compiler error "Member 'allCases' cannot be used on value of protocol type 'CaseIterable'; use a generic constraint instead" which is quite logical, as the actual enum type is unknown".
When I try CaseIterable<T>, I get another error, as CaseIterable is not declared as generic type.
Is there a way?
Couple of changes are necessary:
The return type needs to be Self.AllCases.Index? rather than Int?. In practice, these types will be equivalent, as seen below.
You also need to constrain any types to Equatable, because you need to be equatable in order to use firstIndex(of:). Again, in practice, any CaseIterable will usually be an enum without associated values, meaning it will be equatable automatically.
(Optional change) This function will never return nil, because you're finding one case in a CaseIterable. So you can remove the optionality on the return type (Self.AllCases.Index) and force unwrap.
Example:
public extension CaseIterable where Self: Equatable {
public func ordinal() -> Self.AllCases.Index {
return Self.allCases.firstIndex(of: self)!
}
}
enum Example: CaseIterable {
case x
case y
}
Example.y.ordinal() // 1
type(of: Example.y.ordinal()) // Int
Personally, I'd add that "Ordinal" usually means something different than what you're doing, and I'd recommend changing the function name to elementIndex() or something. But that's an aside.
In the following code, I want to test if x is a SpecialController. If it is, I want to get the currentValue as a SpecialValue. How do you do this? If not with a cast, then some other technique.
The last line there won't compile. There error is: Protocol "SpecialController" can only be used as a generic constraint because it has Self or associated type requirements.
protocol SpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController { // does not compile
Unfortunately, Swift doesn't currently support the use of protocols with associated types as actual types. This however is technically possible for the compiler to do; and it may well be implemented in a future version of the language.
A simple solution in your case is to define a 'shadow protocol' that SpecialController derives from, and allows you to access currentValue through a protocol requirement that type erases it:
// This assumes SpecialValue doesn't have associated types – if it does, you can
// repeat the same logic by adding TypeErasedSpecialValue, and then using that.
protocol SpecialValue {
// ...
}
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController {
print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
}
[Edited to fix: : SpecialValue, not = SpecialValue]
This is not possible. SpecialValueController is an "incomplete type" conceptually so the compiler cannot know. SpecialValueType, although it is constrained by SpecialValue, it is not known until it is determined by any adopting class. So it is a really placeholder with inadequate information. as?-ness cannot be checked.
You could have a base class that adopts SpecialController with a concrete type for SpecialValueController, and have multiple child classes that inherit from the adopting class, if you're still seeking a degree of polymorphism.
This doesn't work because SpecialController isn't a single type. You can think of associated types as a kind of generics. A SpecialController with its SpecialValueType being an Int is a completely different type from a SpecialController with its SpecialValueType being an String, just like how Optional<Int> is a completely different type from Optional<String>.
Because of this, it doesn't make any sense to cast to SpecialValueType, because that would gloss over the associated type, and allow you to use (for example) a SpecialController with its SpecialValueType being an Int where a SpecialController with its SpecialValueType being a String is expected.
As compiler suggests, the only way SpecialController can be used is as a generic constraint. You can have a function that's generic over T, with the constraint that T must be a SpecialController. The domain of T now spans all the various concrete types of SpecialController, such as one with an Int associated type, and one with a String. For each possible associated type, there's a distinct SpecialController, and by extension, a distinct T.
To draw out the Optional<T> analogy further. Imagine if what you're trying to do was possible. It would be much like this:
func funcThatExpectsIntOptional(_: Int?) {}
let x: Optional<String> = "An optional string"
// Without its generic type parameter, this is an incomplete type. suppose this were valid
let y = x as! Optional
funcThatExpectsIntOptional(y) // boom.
I have this kind of code:
protocol MyProtocol {
}
class P1: MyProtocol {
}
class P2: MyProtocol {
}
class C <T: MyProtocol> {
}
Then i need to define a variable to delegate all kinds of C<MyProtocol>:
var obj: C <MyProtocol>
But compile error comes:
Using 'MyProtocol' as a concrete type conforming to protocol 'MyProtocol' is not supported
How can I do?
This code:
class C <T: MyProtocol> { }
Means that C is a generic class that can specialize on any type T that conforms to MyProtocol.
When you declare:
var obj: C <MyProtocol>
You are (I think) trying to say that the var obj will be an instance of C specialized to some type that conforms to MyProtocol,but you can't specialize a generic class on a protocol type, because there is no such thing as a direct concrete instance of a protocol. There can only be instances of a type conforming to the protocol. And there can theoretically be many different types that conform to the protocol. So that notation doesn't really tell the compiler which specific specialization of C to use.
This shouldn't be a problem though, because you can write things like:
var obj: C<P1> = C<P1>()
or
var obj = C<P2>() // type is inferred
And within your class C you can still treat any uses of T as conforming to MyProtocol. So I think this should give you everything you need, as long as you remember that an instance of a generic class must be specialized to a single specific concrete type, not a protocol which could represent many possible concrete types.
You usually don't need to declare type for a Swift's variable. The compiler can infer type in most cases. Also, for generic class, you should let the compiler figure out what the generic classes resolve to:
class C<T: MyProtocol> {
var value: T
init (value: T) {
self.value = value
}
}
var obj = C(value: P1()) // type: C<P1>
// or:
var obj = C(value: P2()) // type: C<P2>