Storing a generic conforming to an associated type inside A collection - swift

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))
}

Related

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)

How is init() relevant in Swift protocol?

Protocols in Swift can declare the init() method in their definition. However, I can't think of any use case where this solves any problem other than forcing the conforming classes to define the init() as in the protocol. We can call the declared methods on the protocol type but init on protocol cannot be used to instantiate its object, which is its only purpose.
What problem does declaring init() method in a protocol solve?
I think the real utility comes when it's used as a constraint in a generic class o function. This is real code from one of my projects.
I declare a protocol with a init:
protocol JSONCreatable {
init(fromJson json: JSON)
}
Then, in a generic function where I return a class that conforms to that protocol:
import SwiftyJSON
extension JSON {
func asObject<T>() -> T? where T: JSONCreatable {
if isEmpty {
return nil
}
return T(fromJson: self)
}
func asArray<T>() -> [T] where T: JSONCreatable {
return array?.map{ json in T(fromJson: json) } ?? []
}
}
This allows me to do things like this:
let user: User = json["user"].asObject()
let results: [Element] = json["elements"].asArray()
It forces class to have init(data: data) from some data, example:
protocol JSONable {
init(data: JSON)
}
forces all classes, that are JSONable to have an initialiser from JSON, so you are always sure, that you can create an instance from JSON.
It's commonly used in order to allow for protocol extensions and generic placeholders constrained to protocols to call the initialiser on the given concrete type that conforms to the protocol. For example, consider RangeReplaceableCollection's default implementation of init<S : Sequence>(_ elements: S):
extension RangeReplaceableCollection {
// ...
/// Creates a new instance of a collection containing the elements of a
/// sequence.
///
/// - Parameter elements: The sequence of elements for the new collection.
public init<S : Sequence>(_ elements: S) where S.Iterator.Element == Iterator.Element {
self.init()
append(contentsOf: elements)
}
// ...
}
Without init() being defined as a protocol requirement of RangeReplaceableCollection, there's no way for the extension to know that we can call init() in order to create a new instance of the conforming type.
But it can also be used directly outside of generics and extensions – for example, it can be used to construct a new instance represented by a given existential metatype (the metatype of 'some concrete type that conforms to a protocol'):
protocol P {
init()
}
struct S : P {
init() {}
}
let s: P = S()
let s1 = type(of: s).init() // creates a new instance of S, statically typed as P.
In this example:
type(of: s) returns the dynamic type of s as P.Type (an existential metatype), as s is statically typed as P. Remember that type(of:) is a (T) -> T.Type operation.
init() constructs a new instance of the underlying concrete type, in this case S.
The new instance is statically typed as P (i.e boxed in an existential container).

Swift: Any Kind of sequence as a function parameter

I have created my custom sequence type and I want the function to accept any kind of sequence as a parameter. (I want to use both sets, and my sequence types on it)
Something like this:
private func _addToCurrentTileset(tilesToAdd tiles: SequenceType)
Is there any way how I can do it?
It seems relatively straightforward, but I can't figure it out somehow. Swift toolchain tells me:
Protocol 'SequenceType' can only be used as a generic constraint because it has Self or associated type requirements, and I don't know how to create a protocol that will conform to SequenceType and the Self requirement from it.
I can eliminate the associatedType requirement with, but not Self:
protocol EnumerableTileSequence: SequenceType {
associatedtype GeneratorType = geoBingAnCore.Generator
associatedtype SubSequence: SequenceType = EnumerableTileSequence
}
Now if say I can eliminate self requirement, then already with such protocol definition other collectionType entities like arrays, sets won't conform to it.
Reference:
my custom sequences are all subclasses of enumerator type defined as:
public class Enumerator<T> {
public func nextObject() -> T? {
RequiresConcreteImplementation()
}
}
extension Enumerator {
public var allObjects: [T] {
return Array(self)
}
}
extension Enumerator: SequenceType {
public func generate() -> Generator<T> {
return Generator(enumerator: self)
}
}
public struct Generator<T>: GeneratorType {
let enumerator: Enumerator<T>
public mutating func next() -> T? {
return enumerator.nextObject()
}
}
The compiler is telling you the answer: "Protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements".
You can therefore do this with generics:
private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) {
...
}
This will allow you to pass in any concrete type that conforms to Sequence into your function. Swift will infer the concrete type, allowing you to pass the sequence around without lose type information.
If you want to restrict the type of the element in the sequence to a given protocol, you can do:
private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) where T.Element: SomeProtocol {
...
}
Or to a concrete type:
private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) where T.Element == SomeConcreteType {
...
}
If you don't care about the concrete type of the sequence itself (useful for mixing them together and in most cases storing them), then Anton's answer has got you covered with the type-erased version of Sequence.
You can use type-eraser AnySequence for that:
A type-erased sequence.
Forwards operations to an arbitrary underlying sequence having the same Element type, hiding the specifics of the underlying SequenceType.
E.g. if you will need to store tiles as an internal property or somehow use its concrete type in the structure of you object then that would be the way to go.
If you simply need to be able to use the sequence w/o having to store it (e.g. just map on it), then you can simply use generics (like #originaluser2 suggests). E.g. you might end up with something like:
private func _addToCurrentTileset<S: SequenceType where S.Generator.Element == Tile>(tilesToAdd tiles: S) {
let typeErasedSequence = AnySequence(tiles) // Type == AnySequence<Tile>
let originalSequence = tiles // Type == whatever type that conforms to SequenceType and has Tile as its Generator.Element
}

Swift: Return class constrained with generic type

I have a basic generic class:
class SharedClass<T> {}
And a builder for it:
class SharedClassBuilder {
func build<T>() -> SharedClass<T>? {
return ...
}
}
What I want to be able to do, is return an instance that inherits SharedClass, and conforms to T. For example:
protocol MyObject {
func doIt()
}
var result: SharedClass<MyObject>? = SharedClassBuilder().build()
result?.doIt()
Unfortunately, the Swift compiler complains that the returned type does not have a member named doIt.
Is there a way to achieve what I'm looking for?
I suspect it's not so much that you want the returned class to be constrained by the generic type, as you're asking the returned class to be an instance of the constrained type. In your snippet, you're expecting the unwrapped result to conform to MyObject. Taking this a step further, it means that the conformance of SharedClass is determined entirely from how it was constructed. As far as I know this isn't supported in Swift.
However, there's nothing stopping you having a member of SharedClass that is a T. Something along the lines of:
class SharedClass<T> {
var v : T?
}
class SharedClassBuilder {
func build<T>() -> SharedClass<T>? {
return SharedClass()
}
}
protocol MyObject {
func doIt()
}
var result: SharedClass<MyObject>? = SharedClassBuilder().build()
result?.v?.doIt()

Swift generic Type inheriting from another generic type

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.