Swift type erasure - for this case? - swift

I have requirement of implementing TypeConverter and later use it as variable type. Inspired by ObjectMapper I have defined following protocol:
protocol TypeConverter {
associatedtype A
associatedtype B
func transformFrom(fromType: A?) -> B?
func transformTo(toType: B?) -> A?
}
Concrete implementation is:
class IntToStringTypeConverter: TypeConverter {
typealias A = Int
typealias B = String
func transformFrom(fromType: Int?) -> String? {
guard let fromType = fromType else { return nil }
return String(fromType)
}
func transformTo(toType: String?) -> Int? {
guard let toType = toType else { return nil }
return Int(toType)
}
}
Because protocol TypeConverter has associatedtype, I cannot declare it as variable, for example: var converter: TypeConverter, but I need such feature. The solution to such case is using typeErasure. By following this link https://medium.com/#NilStack/swift-world-type-erasure-5b720bc0318a it should be possible, but I don't have real idea how.
Here is my try, but its not right :)... Is this even solve-able this way? Or I should use this one: https://appventure.me/2017/12/10/patterns-for-working-with-associated-types ?
class AnyTypeConverter<Y, Z>: TypeConverter {
typealias A = Y
typealias B = Z
private let _transformFrom: (Z?) -> Y?
private let _transformTo: (Y?) -> Z?
init<W: TypeConverter>(_ iFormTypeConverter: W) where W.A == Y, W.B == Z {
self._transformFrom = iFormTypeConverter.transformFrom
self._transformTo = iFormTypeConverter.transformTo
}
func transformFrom(modelType: Y?) -> Z? {
return transformFrom(modelType: modelType)
}
func transformTo(iFormType: Z?) -> Y? {
return transformTo(iFormType: iFormType)
}
}

This is not really a good use for a protocol with associated types. PATs are very complicated tools, and there's really no reason for it in this case at all. You don't even need a type-eraser so much as just a struct:
struct TypeConverter<Model, Form> {
let transformFrom: (Model) -> Form?
let transformTo: (Form) -> Model?
}
let stringToInt = TypeConverter(transformFrom:String.init,
transformTo:Int.init)
stringToInt.transformFrom(123)
stringToInt.transformTo("x")
You of course could make this conform to TypeConverter if you wanted to (and I can update to add that), but I recommend dropping the protocol entirely and just using structs. This is very close to how Formatter works.

After implementing two cells I have found out that I can simplify this thing a bit, and go with only one associated type :). The type used in the cell is actually defined by the UI component - if there is UITextField, then type will be String, if I implement custom stepper, it will be Int (for example). If I want to make my cell generic, then it should work with Any type, for which I can write converter between model and (pre)defined cellType.
protocol FormTypeConverter {
associatedtype FormType
func fromModelToForm(_ value: Any?) -> FormType?
func fromFormToModel(_ value: FormType?) -> Any?
}
With that I can use simple type erasure as follows (source in links in the first post)
struct AnyFormTypeConverter<T>: FormTypeConverter {
// MARK: - Variables
private let fromModelToFormWrapper: (Any?) -> T?
private let fromFormToModelWrapper: (T?) -> Any?
init<Y: FormTypeConverter>(_ formTypeConverter: Y) where Y.FormType == T {
self.fromModelToFormWrapper = formTypeConverter.fromModelToForm
self.fromFormToModelWrapper = formTypeConverter.fromFormToModel
}
func fromModelToForm(_ value: Any?) -> T? {
return fromModelToFormWrapper(value)
}
func fromFormToModel(_ value: T?) -> Any? {
return fromFormToModel(value)
}
}
This case implementation suits excellent. Already implemented two completely different forms in no-time :)

Related

Returning an object that conforms to a generic constraint

I am trying to create a Builder for my ComplexObject:
import Foundation
class ComplexObject {
// lots of stuff
init<ObjectType, T>(_ closure: ((ObjectType) -> T)) {
// lots of init/setup code
}
// other initializers with generics, constructed
// by other Builders than ConcreteBuilder<O> below
}
protocol BuilderType {
associatedtype ObjectType
func title(_: String) -> Self
func build<T>(_ closure: ((ObjectType) -> T)) -> ComplexObject
}
struct Injected<O> {
//...
}
extension ComplexObject {
static func newBuilder<Builder: BuilderType, O>(someDependency: Injected<O>) -> Builder where Builder.ObjectType == O {
// vvvv
return ConcreteBuilder(someDependency: someDependency)
// ^^^^
// Cannot convert return expression of type 'ComplexObject.ConcreteBuilder<O>' to return type 'Builder'
}
struct ConcreteBuilder<O>: BuilderType {
private let dependency: Injected<O>
private var title: String
init(someDependency: Injected<O>) {
self.dependency = someDependency
}
func title(_ title: String) -> ConcreteBuilder<O> {
var builder = self
builder.title = title
return builder
}
func build<T>(_ closure: ((O) -> T)) -> ComplexObject {
return ComplexObject(closure)
}
}
}
but swiftc complains about the return ConcreteBuilder(...) line
Cannot convert return expression of type 'ComplexObject.ConcreteBuilder<O>' to return type 'Builder'
I also tried
static func newBuilder<Builder: BuilderType>(someDependency: Injected<Builder.ObjectType>) -> Builder {
return ConcreteBuilder(someDependency: someDependency)
}
with the same result. I see that I could just expose ConcreteBuilder, but I hoped to be able to hide that implementation detail. What am I missing here?
I'm not sure how to solve this issue, but the root of the problem is that newBuilder(someDependancy:) has a generic type signature, but it's really not generic.
Its return type asserts that function can return an object of any type T: BuilderType where Builder.ObjectType == O, but that's clearly not the case. Asking this function to return any type besides a ConcreteBuilder isn't supported. At best, you could use a force cast, but if someone writes let myBuilder: MyBuilder = ComplexObject.newBuilder(someDependancy: dec), the code would crash (even if MyBuilder satisfies your generic constraints) because you're trying to force cast ConcreteBuilder to MyBuilder.
As far as a solution... I don't have one. Fundamentally you just want to return BuilderType, but I don't think that's possible because it has an associated type.
Will this do ?
return ConcreteBuilder(someDependency: someDependency) as! Builder

Using protocol with associated type inside a generic function in Swift

Hi I'm trying to create a function which accepts a generic type that conforms to a specific protocol, and this protocol has a static builder that return a new instance of the same class (using associated type), after that he returns the new object that was created.
The generic function will return a list of the generic type.
In my efforts to make it compile, I found a solution, but I feel like I cheated, please see the following code:
import UIKit
protocol SomeRougeProtocol {
associatedtype U
static func convert(id: String) -> U
}
class FirstRougeClass: SomeRougeProtocol {
typealias U = FirstRougeClass
let value: String
init(value: String = "") {
self.value = value
}
static func convert(id: String) -> FirstRougeClass {
return FirstRougeClass(value: id)
}
}
class SecondRougeClass: SomeRougeProtocol {
typealias U = SecondRougeClass
let value: String
init(value: String = "") {
self.value = "special \(value)"
}
static func convert(id: String) -> SecondRougeClass {
return SecondRougeClass()
}
}
/// Takes type and generate an array from it.
func superConvert<T: SomeRougeProtocol>(class: T) -> [T.U] {
return [T.convert(id: "1"), T.convert(id: "2"), T.convert(id: "3")]
}
// *** This is the cheasty part, I have to create a disposable object to pass as input, it won't compile otherwise.
let disposableObject = FirstRougeClass()
let a: [FirstRougeClass] = superConvert(class: disposableObject)
a[0].value // Generates "1" in the playground, success!
My question is, if there is a better way to achieve what I done? without using a disposable object would be a big plus haha
Thanks!

Type erasure with method using `Self` as parameter

I'm trying to make a type erasure around Range and ClosedRange but I'm being stuck because they have some methods which take Self as parameter.
All types erasure samples found on the internet don't deal with that case.
Am I trying to do something impossible ?
Here's my implementation (simplified) :
protocol RangeType {
associatedtype _Bound: Comparable
func overlaps(_ other: Self) -> Bool
}
struct AnyRange<Bound: Comparable>: RangeType {
typealias _Bound = Bound
private let _overlaps: (AnyRange<Bound>) -> Bool
init<R: RangeType>(_ range: R) where R._Bound == Bound {
// Cannot assign value of type (R) -> Bool to type (AnyRange<...>) -> Bool
self._overlaps = range.overlaps
}
func overlaps(_ other: AnyRange<Bound>) -> Bool {
return _overlaps(other)
}
}
extension Range: RangeType {
typealias _Bound = Bound
}
extension ClosedRange: RangeType {
typealias _Bound = Bound
}
Before I propose my solution to the problem, first note that what you are trying to do may not be defined. The protocol RangeType ensures that overlaps(_:) is defined for instances whose type is the same as the type that is implementing the function. The type erasure you are attempting to get by mimicking that of AnyIterator cannot be achieved this way because while AnyRange may guarantee that the bounds are identical, the actual underlying types themselves may not be (a requirement of the protocol).
However, that is solved here. If you wish, you may add a special case to handle comparisons between two different types (although evaluation to false as is done here is perhaps desirable)
protocol RangeType {
associatedtype _Bound: Comparable
func overlaps(_ other: Self) -> Bool
}
struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
var range: Range
}
struct AnyRange<Bound: Comparable>: RangeType {
// MARK: RangeType
typealias _Bound = Bound
// Calls the closure of the `_overlaps` property which shields each type and allows self to be passed through
func overlaps(_ other: AnyRange<Bound>) -> Bool { _overlaps.closure(other, self) }
// Shielding structure. Allows us to compare to `AnyRange` instances
private struct OverlapContainer<A, B> {
private(set) var closure: (A, B) -> Bool
init(closure: #escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
// Holds reference to the actual range type. Note that if this is a class type, a strong reference will be created
private let range: Any
/**
Represents this particular type. Should not be called elsewhere in the structure as the cast would fail if `RT != R`
passed to the initiliazer
NOTE: `RT` as the generic type is used instead of `R` to keep us aware of this fact
*/
private nonmutating func rangeComparison<RT: RangeType>() -> RangeComparison<RT, Bound> { RangeComparison<RT, Bound>(range: range as! RT) }
init<R: RangeType>(_ range: R) where R._Bound == Bound {
self.range = range
self._overlaps = .init { other, this in
let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
// If the two types are the same, the comparison can be made
if type(of: other.range).self == R.self {
let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
return thisComparison == otherComparison
}
else { print("Not the same type"); return false } // Otherwise the comparison is invalid
}
}
}
extension Range: RangeType {
typealias _Bound = Bound
}
extension ClosedRange: RangeType {
typealias _Bound = Bound
}
// Examples
let range: Range<Int> = .init(5...8)
let rangeII: ClosedRange<Int> = 1...6
let any: AnyRange<Int> = .init(range)
let anyII: AnyRange<Int> = .init(rangeII)
print(any.overlaps(anyII)) // false.` Range` is not the same type as `ClosedRange`
let rangeIII: ClosedRange<Double> = 3.0...5.5
let rangeIV: ClosedRange<Double> = 1.0...4.0
let anyIII: AnyRange<Double> = .init(rangeIII)
let anyIV: AnyRange<Double> = .init(rangeIV)
print(anyIII.overlaps(anyIV)) // true. Both are 'ClosedRange<Double>' and actually overlap one another
There is a lot here, so let me explain each piece
struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
var range: Range
}
This structure is what is used to represent a given AnyRange type. As I mentioned, comparing any two RangeType instances is not defined if they are not of the same type. This provides a medium to ensure that this is the case as well as making it convenient to equate two AnyRange types through this structure.
The rangeComparison<RT: RangeType>() method uses the type of the RangeType (R) passed into the initializer and casts the range property (set as Any and assigned to the instance passed to the initializer) to this type to create a RangeComparison instance. The range property is what keeps hold of the actual underlying type.
private struct OverlapContainer<A, B> {
private(set) var closure: (A, B) -> Bool
init(closure: #escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
This structure actually allows us to (indirectly) make comparisons between two AnyRange instances through the overlaps(_:) method of AnyRange and a closure. We simply call the _overlaps property's closure property, supplying the other AnyRange instance and a copy of this instance. A copy is used to ensure that the closure can use self without having to use self , as the compiler will complain that "Escaping closure captures mutating self parameter" (hence the reason that OverlapContainer has two generic types).
init<R: RangeType>(_ range: R) where R._Bound == Bound {
self.range = range
self._overlaps = .init { other, this in
let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
// If the two types are the same, the comparison can be made
if type(of: other.range).self == R.self {
let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
return thisComparison == otherComparison
}
else { print("Not the same type"); return false } // Otherwise the comparison is invalid
}
}
Finally, we check if the two comparisons have the same type. If you try to specify each return type as RangeComparison<R, Bound>, it will compile but will crash if the types of each comparison's range property are not the same as the type R inferred from the generic initializer. You also "Cannot explicitly specialize a generic function", and so must specify a type for the result of rangeComparison(). For these two reasons, we check the type and then check if they overlap.

return nil or something similar in method stub

I am taking a data structures in java course and for fun and learning I am trying to write the stuff in Swift. I am trying to implement a protocol but I am having trouble setting up the method stubs. I tried returning nil but that didn't work but now I am getting this error:
"Swift Compiler Error 'E' is not convertible to 'E'"
That is strange. This is code for a generic array based list. This is what I have so far:
struct ArrayLinearList<E>: LinearListADT {
let DEFAULT_MAX_SIZE = 100;
var currentSize: Int
var maxSize: Int
var storage = [E]()
init(sizeOfList: Int) {
currentSize = 0
maxSize = sizeOfList
storage = [E]()
}
mutating func addFirst<E>(obj: E) {
}
mutating func addLast<E>(obj: E) {
}
mutating func insert<E>(obj: E, location: Int) {
}
mutating func remove<E>(location: Int) -> E {
return storage[location] //***This is where I get the above error
}
mutating func remove<E>(obj: E) -> E {
return nil //I tried this but that didn't work either
}
mutating func removeFirst<E>() -> E? {
return nil //I also tried this but that didn't work
}
mutating func removeLast<E>() -> E? {
return nil
}
mutating func get<E>(location: Int) -> E? {
return nil
}
mutating func contains<E>(obj: E) -> Bool {
return false
}
mutating func locate<E>(obj: E) -> Int? {
return nil
}
mutating func clear<E>() {
}
mutating func isEmpty<E>() -> Bool {
}
mutating func size<E>() -> Int {
}
}
EDIT: I just found the mistake. Using the suggestion from Jesper I then found out that I did not write the protocol properly in Swift. Looking at this answer"
how to create generic protocols in swift iOS?
I was able to get it working now. Thank you Jesper!
You shouldn't have a type parameter E on those methods - it will be considered a separate type parameter from the one on the struct. Remove the <E> in those method definitions and the one from the struct itself will be used.
In addition, you may have to add a constraint to E so that you are sure that it implements NilLiteralConvertible (like an Optional), otherwise you can't return nil from a function that is supposed to return a E.
I just found the mistake. Using the suggestion from Jesper I then found out that I did not write the protocol properly in Swift. Looking at this answer"
How to create generic protocols in Swift?
I was able to get it working now. Thank you Jesper!

How to create an array of instances of a subclass from a superclass

From this answer, I know that I can create an instance of a subclass from a superclass. Yet, I can't figure out how to create an array of the subclass from the superclass.
Drawing on the above example, here's my best shot so far:
class Calculator {
func showKind() { println("regular") }
required init() {}
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("\(model) - Scientific") }
required init() {
super.init()
}
}
extension Calculator {
class func createMultiple<T:Calculator>(num: Int) -> T {
let subclass: T.Type = T.self
var calculators = [subclass]()
for i in 0..<num {
calculators.append(subclass())
}
return calculators
}
}
let scis: [ScientificCalculator] = ScientificCalculator.createMultiple(2)
for sci in scis {
sci.showKind()
}
With that code, the line var calculators = [subclass]() shows the error Invalid use of '()' to call a value of non-function type '[T.Type]'.
How can I return an array of ScientificCalculators from Calculator.createMultiple?
You were on the right track but you've made some mistakes.
First you need to return a array of T and not just a single element. So you need to change the return type from T to [T]:
class func createMultiple<T:Calculator>(num: Int) -> [T] {
Also you can just use T to initialize new instances of your subclass like that:
var calculators:[T] = [T]()
But the other parts are correct. So you final method would look like that:
extension Calculator {
class func createMultiple<T:Calculator>(num: Int) -> [T] {
let subclass: T.Type = T.self
var calculators = [T]()
for i in 0..<num {
calculators.append(subclass())
}
return calculators
}
}
Edit
If you are using Swift 1.2 you don't have to deal with subclass anymore and you will be able to use T instead like shown in Airspeeds answer.
calculators.append(T())
EDIT: this behaviour appears to have changed in the latest Swift 1.2 beta. You shouldn’t need to use T.self. T is the type you want to create. But if you are using 1.1, it appears not to work (even if T is the subtype, it creates the supertype), and using the metatype to create the type works around this problem. See end of answer for a 1.1 version.
You don’t need to mess with subclass: T.Type = T.self. Just use T – that itself is the type (or rather, a placeholder for whatever type is specified by the caller):
extension Calculator {
// you meant to return an array of T, right?
class func createMultiple<T: Calculator>(num: Int) -> [T] {
// declare an array of T
var calculators = [T]()
for i in 0..<num {
// create new T and append
calculators.append(T())
}
return calculators
}
}
btw, you can replace that for loop with map:
class func createMultiple<T: Calculator>(num: Int) -> [T] {
return map(0..<num) { _ in T() }
}
If you are still on Swift 1.1, you need to use T.self to work around a problem where the subtype is not properly created:
extension Calculator {
// only use this version if you need this to work in Swift 1.1:
class func createMultiple<T: Calculator>(num: Int) -> [T] {
let subclass: T.Type = T.self
return map(0..<num) { _ in subclass() }
}
}