Is there a way to constrain `Self` to a generic type?
For the generic type Owl<T>
you are allowed to have the constraint where Self: Owl<String>
, but it will only work in contexts where the concrete type information is available.
To make it clear what is happening, consider this:
let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"
As opposed to this:
let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"
When Swift creates the protocol witness tables for the types Owl<T>
and FlappyBird
, it has to act differently for each one because Owl
is generic. If it doesn't have the concrete type information, i.e. Owl<String>
at the call site, it must use the default implementation for Owl<T>
. This "loss" of type information is happening when you are inserting the owls into the array of type [Bird]
.
In the case of FlappyBird
, since there is only one possible implementation (since it's not generic), the compiler produces a witness table with the "expected" method reference, which is print("FlappyBird: \(name)")
. Since FlappyBird
is not generic, its witness table doesn't need any reference to the unconstrained default implementation of doSomething()
and can therefore correctly call the the constrained implementation even when the concrete type information is missing.
To make it clear that the compiler "needs" to have the fall back behavior for a generic type, you can remove the Bird
conformance from Owl<T>
and try to rely solely on the constrained default implementation. This will result in a compilation error with an error that is, as usual with Swift, highly misleading.
Value of type 'Owl' has no member 'doSomething'
Basically, it seems the witness table can't be built because it requires the existence of an implementation that will work for all types T
on Owl
.
References
- https://forums.swift.org/t/swifts-method-dispatch/7228
- https://developer.apple.com/videos/play/wwdc2016/416/
Is it possible to constrain `Self` to a generic parameter?
I managed to do it using:
func doSomething<T: Delegate>(with bar: Bar<T>) -> Foo where T.Foo == Foo
It seems using T: Self
does not work here and you have to add the condition for equal associated types.
Using self as a Generic Type Constraint
public class Example : Singleton<Example> {}
The constraint on Singleton<T>
is T : Singleton<T>
. Substitute T := Example
, and you get the constraint Example : Singleton<Example>
, which holds because that's how we declared Example
. No issues.
public class Fraud : Singleton<Example> {}
The constraint on Singleton<T>
is T : Singleton<T>
. Substitute T := Example
, and you get the constraint Example : Singleton<Example>
, which holds because that's how we declared Example
. No issues.
There's no reason for Fraud
to not be valid, so it is valid. After all, the point of F-bounded polymorphism is method chaining, and you can chain methods on Fraud
just as well as you can on Example
. It might not do the same thing, but that's the same kind of problem you'd get if you created a new IDictionary
that ignored every second call to Add
: you can't use the type system to enforce everything (unless you're Coq); you can't always stop external entities from doing things they shouldn't do.
Generic class with self-referencing type constraint
You can cast 'this' to T:
Bar((T)this);
This however will fail if you have the following:
public class MyFoo : Foo<MyFoo> { }
public class MyOtherFoo : Foo<MyFoo> { }
Because 'MyOtherFoo' is not an instance of 'MyFoo'. Take a look at this post by Eric Lippert, one of the designers of C#.
Swift generic function constrained by class generic
Therefore, the error is incorrect. ...Right?
Well... the current constraints system is not yet powerful enough to allow this kind of constraints. The good news is that generalized supertype constraints is on the Swift road map as described in the Generics Manifesto.
The below won't compile either, for the same reasons:
func test<A, B>(_ a: A, _ b: B) where A: AnyObject, B: A {
// ^^^ Type 'B' constrained to non-protocol, non-class type 'A'
}
Neither the example from the manifesto:
protocol P {
associatedtype Base
associatedtype Derived: Base
}
Unfortulately, you'll have to wait until this feature is available to Swift, to make it work as you want.
Go generics: self-referring interface constraint
Combine your two attempted interfaces together:
type Shape[ST any] interface {
*Circle | *Square
Bigger() ST
Smaller() ST
}
And then instantiate the constraint of process
with the type parameter itself:
func process[ST Shape[ST]](s ST) ST {
return s.Bigger().Smaller()
}
- Adding the union element
*Circle | *Square
intoShape[ST any]
means that only those two types will be able to implement the interface - Then using the type parameter in the method signature, like
Bigger() ST
, means that whichever type is passed has a method that returns itself.
If you want to keep ShapeType
as a separated interface, you can write Shape
as:
type Shape[ST any] interface {
ShapeType
Bigger() ST
Smaller() ST
}
You can also use process
method with type inference, without any issue:
func main() {
c1 := NewCircle(3)
c2 := process(c1)
fmt.Println(c2.Radius()) // prints 3 as expected
fmt.Printf("%T\n", c2) // *main.Circle
s1 := NewSquare(6)
s2 := process(s1)
fmt.Println(s2.Side()) // prints 6 as expected
fmt.Printf("%T\n", s2) // *main.Square
}
Final playground: https://go.dev/play/p/_mR4wkxXupH
Related Topics
Uitableviewcell Subclass Wrong Image in Cell or Old Image Bug
Swiftui Map Overlays Without UIviewrepresentable
How to Provide Default Implementation of an Objective-C Protocol in a Swift Protocol Extension
Underlying Type for Tuple in Swift
Uibutton Borders Function Only Gives Back White Borders
Way to Check If Up or Down Button Is Pressed with Nsstepper
Swiftui Published Updates Not Refreshing
Swift Compile Error, Subclassing Nsvalue, Using Super.Init(Nonretainedobject:)
Root Class of All Classes in Swift
Compiler Segmentation Fault While Using Set in Swift
Swift: Trunc a Floating Number to Show It in a Label
Swiftui Go Back Programmatically from Representable to View
How to Display an Alert Controller When Nsdate == to a Time (For Example 12:00 Am) in Swift
Dictionary Becomes Nil When Trying to Pass Back Cllocation Object to iOS App Extension
Swift: Switch Statement Fallthrough Behavior
How to Set a Known Position and Orientation as a Starting Point of Arkit
(Key: Anyobject, Value: Anyobject)' Does Not Have a Member Named 'subscript'