Swift generic Type inheriting from another generic type - swift

I am trying to get the code below working in a playground:
public protocol SomeTypeProtocol {
associatedtype T
func convertTo<TNew:SomeTypeProtocol>()-> TNew
}
public class SomeClass<T>: SomeTypeProtocol{
var item:T
public init(item:T)
{
self.item = item
}
public func convertTo<TNew:SomeTypeProtocol where TNew.T : T>()-> TNew
{
return SomeClass<TNew.T>(item: item as! TNew.T)
}
}
Basically, I have a protocol with an associated type T, and a class conforming to that protocol with generic type T, and I need to implement the convertTo function that simply converts the class's type T to another type TNew.T which should be a subclass from T.
I have this implemented in other languages like C# and Java, so I am not sure why I can't get it working in Swift.
I am getting the following errors:
1) error: type 'TNew.T' constrained to non-protocol type 'T'
public func convertTo<TNew:SomeTypeProtocol where TNew.T : T>()-> TNew
2) error: cannot invoke initializer for type 'SomeClass<TNew.T>' with an argument list of type '(item: TNew.T)'
return SomeClass<TNew.T>(item: item as! TNew.T)
3) note: expected an argument list of type '(item: T)'
return SomeClass<TNew.T>(item: item as! TNew.T)
4)note: protocol requires nested type 'T'
associatedtype T

TNew.T : T
This is where the problem lies.
The colon, :, is used to declare a conformance constraint, not equality. You need to rewrite this as TNew.T == T if you want to equate the two types TNew.T and T.

I believe the closest you can get to doing something like that is this:
public func convertTo<TNew where TNew : T>()-> SomeClass<TNew>
{
return SomeClass<TNew>(item: item as! TNew)
}
But I don't believe you can't enforce it being only a subclass and not the same class.
Be careful doing this type of thing with generics. If you're needing to constantly change the Type of your generic, you might want to consider using a different design.

Related

Swift protocols with generic arguments

Swift noob here. Consider this Swift 5.7 code:
import Foundation
// This is according to the grammar.
protocol TestProtocol1 {
associatedtype T
associatedtype U
}
// Not allowed by the grammer, but still compiles.
protocol TestProtocol2<T> {
associatedtype T
associatedtype U
}
// Doesn't seem to matter if I add one or both type arguments.
protocol TestProtocol3<T, U> {
associatedtype T
associatedtype U
}
// This is fine. As expected.
class TestClass1 : TestProtocol1 {
typealias T = Int
typealias U = Bool
}
// Fine too. Even though I don't specify the type arguments.
class TestClass2 : TestProtocol2 {
typealias T = Int
typealias U = Bool
}
// error: cannot inherit from protocol type with generic argument 'TestProtocol3<Int, Bool>'
class TestClass3 : TestProtocol3<Int, Bool> {
typealias T = Int
typealias U = Bool
}
Questions:
Is there any semantic difference between the three protocol definitions?
Why does it compile when declaring the associated types as generic arguments when the grammar doesn't allow it?
Why is it useful to (probably redundantly) add type arguments to protocols? For
example, protocol Sequence<Element> does this too.
The "generic parameters" that you are seeing are the protocols' primary associated types, proposed in SE-0346, implemented in Swift 5.7, I suppose the grammar section in the language reference just hasn't been updated yet.
The three protocol declarations are semantically different, in that they have different primary associated types. When using the protocol in certain positions, primary associated types are what you can directly specify in <...>, rather than specify them somewhere else like in a where clause. For example, when using the protocol as a generic constraint:
func foo<P: TestProtocol2<Int>>(p: P) { ... }
is syntactic sugar for:
func foo<P: TestProtocol2>(p: P) where P.T == Int { ... }
The former is just a little more concise :)
You cannot do something similar with TestProtocol1, because it doesn't have primary associated types.
For TestProtocol3, you must specify both primary associated types:
func foo<P: TestProtocol3<Int, Bool>>(p: P) { ... }
According to the SE proposal, this syntax was also planned to be usable in the protocol conformance clause of a concrete type, like in your code:
class TestClass3 : TestProtocol3<Int, Bool> { // does not compile
However, this feature did not get added for some reason. You can still use it in the inheritance clause of a protocol though:
protocol TestProtocol4: TestProtocol3<Int, Bool> { } // works
See the SE proposal for more details.
For the first question Is there any semantic difference between the three protocol definitions?
I dont think so . When you create a protocol with <..> after protocol name , Protocols think that the name giving between <> is a associated type or types but you must add that associatedtype name with the same in <>
For example if you delete U TestProtocol3 like
protocol TestProtocol3<T, U>{
associatedtype T
}
you will get an error says : An associated type named 'U' must be declared in the protocol 'TestProtocol3' or a protocol it inherits.In the opposite way if you delete U in protocol TestProtocol3<T, U> like protocol TestProtocol3<T> , will not give an any error.
Here is my answer to your questions:
Is there any semantic difference between the three protocol definitions?
The only difference it's that TestProtocol1 has the correct declaration.
Why does it compile when declaring the associated types as generic arguments when the grammar doesn't allow it?
I made the test and it doesn't compile for me! kind weird 🤔
Why is it useful to (probably redundantly) add type arguments to protocols? For example, protocol Sequence does this too.
I'm not sure that I understand your question here but, the Sequence protocol is declared in the same way you had declared your TestProtocol1
/// A sequence should provide its iterator in O(1). The `Sequence` protocol
/// makes no other requirements about element access, so routines that
/// traverse a sequence should be considered O(*n*) unless documented
/// otherwise.
public protocol Sequence {
/// A type representing the sequence's elements.
associatedtype Element where Self.Element == Self.Iterator.Element
/// A type that provides the sequence's iteration interface and
/// encapsulates its iteration state.
associatedtype Iterator : IteratorProtocol
...
}
``

Swift Implementation generic function

I'm a developer on Java and I'm trying to write in Swift the same solution that I have in Java code.
Is it possible to do this on Swift?
Example Java:
public interface Converter<S,T> {
T convert(S in)
}
public class CarConverterToDTO implements Converter<Car, CarDTO> {
#Override
public CarDTO convert(Car in) {
.....
}
}
Example Swift:
protocol Converter {
func convert<IN, OUT>(in: IN) -> OUT
}
How it would be the implementation?
Thanks!!!
What appears to be a simple question is actually the tip of a rather large and unpleasant iceberg…
I'm going to start by giving you what is probably the real solution to your problem:
class Converter<Input, Output> {
func convert(_ input: Input) -> Output {
fatalError("subclass responsibility")
}
}
struct Car { }
struct CarDTO { }
class DTOCarConverter: Converter<Car, CarDTO> {
override func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}
Above, I've translated your Java Converter interface into a Swift class instead of a Swift protocol. That's probably what you want.
Now I'll explain why.
A programmer coming from Java to Swift might think that a Swift protocol is the equivalent of a Java interface. So you might write this:
protocol Converter {
associatedtype Input
associatedtype Output
func convert(_ input: Input) -> Output
}
struct Car { }
struct CarDTO { }
class /* or struct */ DTOCarConverter: Converter {
func convert(_ input: Car) -> CarDTO {
return CarDTO()
}
}
Okay, now you can create a converter and convert something:
let converter = DTOCarConverter()
let car = Car()
let dto = converter.convert(car)
But you're going to run into a problem as soon as you want to write a function that takes a Converter as an argument:
func useConverter(_ converter: Converter) { }
// ^
// error: protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements
“Well, duh,” you say, “you forgot the type arguments!” But no, I didn't. Swift doesn't allow explicit type arguments after a protocol name:
func useConverter(_ converter: Converter<Car, CarDTO>) { }
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'
I don't want to get into why you can't do this. Just accept that a Swift protocol is not generally equivalent to a Java interface.
A Swift protocol with no associated types and no mention of Self is, generally, equivalent to a non-generic Java interface. But a Swift protocol with associated types (or that mentions Self) is not really equivalent to any Java construct.
When discussing this problem, we often use the acronym “PAT”, which stands for “Protocol with Associated Types” (and includes protocols that mention Self). A PAT doesn't define a type that you can use as a function argument, return value, or property value. There's not much you can do with a PAT:
You can define a subprotocol. For example, Equatable is a PAT because it defines the == operator to take two arguments of type Self. Hashable is a subprotocol of Equatable.
You can use a PAT as a type constraint. For example, Set is a generic type. Set's type parameter is named Element. Set constrains its Element to be Hashable.
So you can't write a function that takes a plain Converter as an argument. But you can write a function that takes any implementation of Converter as an argument, by making the function generic:
func useConverter<MyConverter: Converter>(_ converter: MyConverter)
where MyConverter.Input == Car, MyConverter.Output == CarDTO
{ }
That compiles just fine. But sometimes it's inconvenient to make your function generic.
And there's another problem that this doesn't solve. You might want a container that holds various Converters from Car to CarDTO. That is, you might want something like this:
var converters: [Converter<Car, CarDTO>] = []
// ^ ~~~~~~~~~~~~~
// error: cannot specialize non-generic type 'Converter'
We can't fix this by making converters generic, like we did with the useConverter function.
What you end up needing is a “type-erased wrapper”. Note that “type-erased” here has a different meaning that Java's “type erasure”. In fact it's almost the opposite of Java's type erasure. Let me explain.
If you look in the Swift standard library, you'll find types whose names start with Any, like AnyCollection. These are mostly “type-erased wrappers” for PATs. An AnyCollection conforms to Collection (which is a PAT), and wraps any type that conforms to Collection. For example:
var carArray = Array<Car>()
let carDictionary = Dictionary<String, Car>()
let carValues = carDictionary.values
// carValues has type Dictionary<String, Car>.Values, which is not an array but conforms to Collection
// This doesn't compile:
carArray = carValues
// ^~~~~~~~~
// error: cannot assign value of type 'Dictionary<String, Car>.Values' to type '[Car]'
// But we can wrap both carArray and carValues in AnyCollection:
var anyCars: AnyCollection<Car> = AnyCollection(carArray)
anyCars = AnyCollection(carValues)
Note that we have to explicitly wrap our other collections in AnyCollection. The wrapping is not automatic.
Here's why I say this is almost the opposite of Java's type erasure:
Java preserves the generic type but erases the type parameter. A java.util.ArrayList<Car> in your source code turns into a java.util.ArrayList<_> at runtime, and a java.util.ArrayList<Truck> also becomes a java.util.ArrayList<_> at runtime. In both cases, we preserve the container type (ArrayList) but erase the element type (Car or Truck).
The Swift type-erasing wrapper erases the generic type but preserves the type parameter. We turn an Array<Car> into an AnyCollection<Car>. We also turn a Dictionary<String, Car>.Values into an AnyCollection<Car>. In both cases, we lose the original container type (Array or Dictionary.Values) but preserve the element type (Car).
So anyway, for your Converter type, one solution to storing Converters in a container is to write an AnyConverter type-erased wrapper. For example:
struct AnyConverter<Input, Output>: Converter {
init<Wrapped: Converter>(_ wrapped: Wrapped) where Wrapped.Input == Input, Wrapped.Output == Output {
self.convertFunction = { wrapped.convert($0) }
}
func convert(_ input: Input) -> Output { return convertFunction(input) }
private let convertFunction: (Input) -> Output
}
(There are multiple ways to implement type-erased wrappers. That is just one way.)
You can then use AnyConverter in property types and function arguments, like this:
var converters: [AnyConverter<Car, CarDTO>] = [AnyConverter(converter)]
func useConverters(_ converters: [AnyConverter<Car, CarDTO>]) {
let car = Car()
for c in converters {
print("dto = \(c.convert(car))")
}
}
But now you should ask: what's the point? Why bother making Converter a protocol at all, if I'm going to have to use a type-erased wrapper? Why not just use a base class to define the interface, with subclasses implementing it? Or a struct with some closures provided at initialization (like the AnyConverter example above)?
Sometimes, there's not a good reason to use a protocol, and it's more sensible to just use a class hierarchy or a struct. So you should take a good look at how you're implementing and using your Converter type and see if a non-protocol approach is simpler. If it is, try a design like I showed at the top of this answer: a base class defining the interface, and subclasses implementing it.
The Swift equivalent to your Java code looks like this.
protocol Converter {
associatedtype Input
associatedtype Output
func convert(input: Input) -> Output
}
class CarConverterToDTO: Converter {
typealias Input = Car
typealias Output = CarDTO
func convert(input: Car) -> CarDTO {
return CarDTO()
}
}
Explanation
The equivalent to a generic Java interface in Swift, would be a protocol with associatedtypes.
protocol Converter {
associatedtype Input
associatedtype Output
}
To create an implementation of that protocol, the implementation must specify which types the associated types maps to, using typealias.
class CarConverterToDTO: Converter {
typealias Input = Car
typealias Output = CarDTO
}
Type Erasure
If you try to use to this approach, you may run into the issue of trying to store an instance of your generic protocol in a variable or property, in which case you will get the compiler error:
protocol 'Converter' can only be used as a generic constraint because it has Self or associated type requirements
The way to solve this issue in Swift, is by using type erasure, where you create a new implementation of your generic protocol, that itself is a generic type (struct or class), and uses a constructor accepting a generic argument, matching your protocol, like so:
struct AnyConverter<Input, Output>: Converter {
// We don't need to specify type aliases for associated types, when the type
// itself has generic parameters, whose name matches the associated types.
/// A reference to the `convert(input:)` method of a converter.
private let _convert: (Input) -> Output
init<C>(_ converter: C) where C: Converter, C.Input == Input, C.Output == Output {
self._convert = converter.convert(input:)
}
func convert(input: Input) -> Output {
return self._convert(input)
}
}
This is usually accompanied by an extension function on the generic protocol, that performs the type erasure by creating an instance of AnyConverter<Input, Output> using self, like so:
extension Converter {
func asConverter() -> AnyConverter<Input, Output> {
return AnyConverter(self)
}
}
Using type erasure, you can now create code that accepts a generic Converter (by using AnyConverter<Input, Output>), that maps Car to CarDTO:
let car: Car = ...
let converter: AnyConverter<Car, CarDTO> = ...
let dto: CarDTO = converter.convert(input: car)

Storing a generic conforming to an associated type inside A collection

I am trying to store a generic who uses an an associated type, however when trying to create a type which should conform to the generic type I describe in the generic list at the top of class A, but I get the error.
"Cannot invoke 'append' with an argument list of type '(B)'"
How can I properly declare the generic so that this code works?
class A<DataType: Any, AssociatedType: Runable> where
AssociatedType.DataType == DataType {
var array = Array<AssociatedType>()
func addAssociatedValue(data: DataType) {
array.append(B(data: data))
}
func runOnAll(with data: DataType) {
for item in array {
item.run(with: data)
}
}
}
class B<DataType>: Runable {
init(data: DataType) { }
func run(with: DataType) { }
}
protocol Runable {
associatedtype DataType
func run(with: DataType)
}
I am also using Swift 4.2 so if there is a solution that uses one of the newer Swift features that will also work as a solution.
B conforms to Runnable, yes, but you can't put it into an array that's supposed to store AssociatedTypes. Because the actual type of AssociatedType is decided by the caller of the class, not the class itself. The class can't say, "I want AssociatedType to always be B". If that's the case, you might as well remove the AssociatedType generic parameter and replace it with B. The caller can make AssociatedType be Foo or Bar or anything conforming to Runnable. And now you are forcing to put a B in.
I think you should rethink your model a bit. Ask yourself whether you really want AssociatedType as a generic parameter.
You could consider adding another requirement for Runnable:
init(data: DataType)
And add required to B's initializer. This way, you could write addAssociatedValue like this:
func addAssociatedValue(data: DataType) {
array.append(AssociatedType(data: data))
}

Inheritance, Generics, and Protocols in Swift

I have a generic class of the form:
class BaseClass<T> {
var prop: T
...
}
I then have multiple subclasses of the form:
class SubClassOne: BaseClass<SomeSubClass> {
...
}
class SubClassTwo: BaseClass<SomeOtherSubClass> {
...
}
Where the type parameters SomeSubClass and SomeOtherSubClass both inherit from a common base class SomeBaseClass.
I now want to define a variable to store instances of both SubClassOne and SubClassTwo. I have tried many possibilities:
var obj: BaseClass
var obj: BaseClass<SomeBaseClass>
var obj: BaseClass<Any>
But the first attempt results in the error Reference to generic type 'BaseClass' requires arguments in <...>, and the other two result in the error Cannot assign value of type 'SubClassOne' to type ... when trying to assign a value. I even tried to trick the Swift compiler into inferring the type for me by initializing an array:
var testArray = [SubClassOne(), SubClassTwo()]
But even this failed, resulting in the error Heterogeneous collection literal could only be inferred to [Any]; add explicit type annotation if this is intentional. Indeed, the only type annotation that successfully allows storage of both SubClasses is Any or AnyObject. Is it possible to store these instances with a more specific type? If not, why?
The reason it's important to do so is that I ultimately want to get the property prop from the stored variable obj. I am unable to do so if obj is stored as Any. I am also unable to simply cast it to SubClassOne or SubClassTwo because the method itself where I am trying to access the properties is a generic method, and which of SubClassOne or SubClassTwo to cast to depends on the generic type parameter of the method:
func castObj<T>(asType: T.Type) {
(self.obj as? T).prop
}
Which would be called as: castObj(asType: SubClassOne.self) or castObj(asType: SubClassTwo.self). However, we run into the same problem: the only generic type parameter constraint I can define that accepts both SubClassOne and SubClassTwo is Any, and then the Swift compiler complains: Value of type 'T' has no member 'prop'.
As a workaround I tried to define a protocol that encapsulates the desired property:
protocol HasProp {
var prop: SomeBaseClass { get }
}
Then I added this to the declaration of SubClassOne and SubClassTwo. However this resulted in still another error: Type 'SubClassOne' does not conform to protocol 'HasProp'. This confuses me as well, since SubClassOne and SubClassTwo both inherit prop from BaseClass<SomeSubClass> and so actually do conform to the protocol.
In summary:
Is it possible to store instances of SubClassOne and SubClassTwo with a more specific type that gives access to properties of BaseClass? If not, why?
Why do the SubClasses not conform to the protocol as expected?
How can I change the design to attain my desired behavior?
The problem is that at the moment the function castObj has no type constraints for its generic parameter, T. By giving a type constraint of BaseClass you should be fine, since BaseClass has both properties.
func castObj<T: BaseClass>(asType: T.Type) {
(self.obj as? T).propOne
(self.obj as? T).propTwo
}
In your example, the type of propTwo was common to both subclasses and the type of propOne was specialized. Make your design reflect that.
[was]
class BaseClass<T,U> {
var propOne: T
var propTwo: U
...
}
class SubClassOne: BaseClass<SomeSubClass, SomeClass> {}
class SubClassTwo: BaseClass<SomeOtherSubClass, SomeClass> {}
[could be]
class BaseClass<U> {
var propTwo: U
...
}
class SubClassOne<T>: BaseClass<SomeClass> {
var propOne: T
...
}
class SubClassTwo<T>: BaseClass<SomeClass> {
var propOne: T
...
}
The point is to keep common things in the base class and compose your specializations.
There's a fundamental misconception that SubclassOne and SubclassTwo are in the same inheritance hierarchy. Because of the generic type, they inherit from different base classes. You cannot mix and match them.
Think about it. With inheritance you should be able to use any subclass anywhere where you have the base class, so in your test example:
var testArray = [SubClassOne(), SubClassTwo()]
What type would the right hand side of the following expressions have to be?
testArray[0].prop = something
And this one
testArray[1].prop = something;
In SubClassOne, the type of prop is SomeSubClass and in SubClassTwo the type of prop must be SomeOtherSubClass.
The only way for you to get this to work is for prop to be declared as SomeBaseClass and that removes the necessity for BaseClass to be generic.
Edit
Why doesn't the protocol work?
The problem with the protocol is that you define the property as having the type of the base class but it is read/write. A property in an implementation of the protocol cannot fulfill the contract with a property that is specialised to one of the subclasses because other bits of code need to be able to assign any instance of the base class to the property.
protocol MyProtocol
{
var prop: BaseClass
}
struct MyImplementation: MyProtocol
{
var prop: SubClass
}
class BaseClass {}
class SubClass: BaseClass {}
class DifferentSubClass: BaseClass {}
var instance: MyProtocol = MyImplementation()
instance.prop = DifferentSubClass()
// Should be legal because the protocol says so but the type of prop in instance is SubClass.

In Swift, how to cast to protocol with associated type?

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.