Type erasure with method using `Self` as parameter - swift

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.

Related

Syntactic help: constraining functions to generic class

I have a structure that I simplified like this:
protocol Protocol {
associatedtype T
var common: T { get }
}
class Superclass<T>: Protocol {
let common: T
init(common: T) { self.common = common }
}
class IntClass<T>: Superclass<T> {
let int = 5
}
class StringClass<T>: Superclass<T> {
let string = "String"
}
class Example<P: Protocol> {
let object: P
init(object: P) { self.object = object }
func common() -> P.T { object.common }
func int() -> Int where P == IntClass<Any> { object.int }
func string() -> String where P == StringClass<Any> { object.string }
}
I would like to create objects of the generic class Example where some of them contain an object of the also generic IntClass while others have a generic StringClass object. Now I’d like to add accessors on Example for IntClass and StringClass specific properties (so I don’t have to access them directly). They would need to be constrained to the respective class. These would be int() and string() in my example.
My example doesn’t work like intended though:
let intExample = Example(object: IntClass(common: Double(1)))
// 👍 (= expected)
intExample.common() // Double 1
// 👍 (= expected)
intExample.string() // Instance method 'string()' requires the types 'IntClass<Float>' and 'StringClass<Any>' be equivalent
// 👎 (= not expected)
intExample.int() // Instance method 'int()' requires the types 'IntClass<Float>' and 'IntClass<Any>' be equivalent
I also tried:
func int() -> Int where P == IntClass<P.T> { object.int }
With these compiler complaints:
- Generic class 'Example' requires that 'P' conform to 'Protocol'
- Same-type constraint 'P' == 'IntClass<P.T>' is recursive
- Value of type 'P' has no member 'int'
And I tried:
func string<T>() -> String where P == StringClass<T> { object.string }
which, when using like intExample.string() results in Generic parameter 'T' could not be inferred (next to Instance method 'string()' requires the types 'IntClass<Double>' and 'StringClass<T>' be equivalent).
I don’t want string() to appear on an Example<IntClass> object in code completion.
Is there a syntax to accomplish what I want (anything with typealias?) or would I have to navigate around that problem?
Since the properties you are trying to access here doesn't depend on the type parameter T of IntClass or StringClass, you can write two non generic protocols HasInt and HasString:
protocol HasInt {
var int: Int { get }
}
protocol HasString {
var string: String { get }
}
extension IntClass: HasInt { }
extension StringClass: HasString { }
Then you can constrain to the protocols, and access int and string through the protocol instead:
func int() -> Int where P: HasInt { object.int }
func string() -> String where P: HasString { object.string }

How to extract `rawValue` from `Any` if value is `RawRepresentable`

I'm looking for something like:
func extractRawValue(fromPossibleRawRepresentable value: Any) -> Any? {
return (value as? RawRepresentable)?.rawValue
}
I don't mind if extracted RawValue needs to be static type...
func extractRawValue<T: RawRepresentable, U>(fromPossibleRawRepresentable value: Any, expecting: U.Type) -> U? where T.RawValue == U {
return (value as? T)?.rawValue
}
Context: I want to collect rather raw than actual values in mirror.
let d = Mirror(reflecting: self).children.reduce(into: [String: String](), {
guard let label = $1.label else {
return
}
$0[label] = extractRawValue(fromPossibleRawRepresentable: $1.value)
}
The problem is that RawRepresentable has an associatedtype, so you can't assign Any to it. You also can't use it as a generic type because then you have to use the concrete type itself in the function signature, which defeats the purpose.
You can circumvent these problems fairly easily using:
protocol RawString {
var rawValue: String { get }
}
This will allow you to extract the values using:
func extractRawValue(value: Any) -> String? {
return (value as? RawString)?.rawValue
}
For any type from which you want to extract the rawValue as a String, just add to it conformance to RawString, e.g.
enum Foo: String, RawString {}
// or
struct StringContainer: RawString {
var rawValue: String
}
The downsides to this approach is that you need to explicitly mark each type to conform to RawString, but unfortunately I can't see any other way.

Returning a nil from an optional generic extension

Here's something I'm playing with. The problem is that I have a container class that has a generic argument which defines the type returned from a closure. I want to add a function that is only available if they generic type is optional and have that function return a instance containing a nil.
Here's the code I'm currently playing with (which won't compile):
open class Result<T>: Resolvable {
private let valueFactory: () -> T
fileprivate init(valueFactory: #escaping () -> T) {
self.valueFactory = valueFactory
}
func resolve() -> T {
return valueFactory()
}
}
public protocol OptionalType {}
extension Optional: OptionalType {}
public extension Result where T: OptionalType {
public static var `nil`: Result<T> {
return Result<T> { nil } // error: expression type 'Result<T>' is ambiguous without more context
}
}
Which I'd like to use like this:
let x: Result<Int?> = .nil
XCTAssertNil(x.resolve())
Any idea how to make this work?
I don't think you can achieve this with a static property, however you can achieve it with a static function:
extension Result {
static func `nil`<U>() -> Result where T == U? {
return .init { nil }
}
}
let x: Result<Int?> = .nil()
Functions are way more powerful than properties when it comes to generics.
Update After some consideration, you can have the static property, you only need to add an associated type to OptionalType, so that you'd know what kind of optional to have for the generic argument:
protocol OptionalType {
associatedtype Wrapped
}
extension Optional: OptionalType { }
extension Result where T: OptionalType {
static var `nil`: Result<T.Wrapped?> {
return Result<T.Wrapped?> { nil }
}
}
let x: Result<Int?> = .nil
One small downside is that theoretically it enables any kind of type to add conformance to OptionalType.

Swift type erasure - for this case?

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

Swift protocol with constrained associated type error "Type is not convertible"

I have created 2 protocols with associated types. A type conforming to Reader should be able to produce an instance of a type conforming to Value.
The layer of complexity comes from a type conforming to Manager should be able to produce a concrete Reader instance which produces a specific type of Value (either Value1 or Value2).
With my concrete implementation of Manager1 I'd like it to always produce Reader1 which in turn produces instances of Value1.
Could someone explain why
"Reader1 is not convertible to ManagedReaderType?"
When the erroneous line is changed to (for now) return nil it all compiles just fine but now I can't instantiate either Reader1 or Reader2.
The following can be pasted into a Playground to see the error:
import Foundation
protocol Value {
var value: Int { get }
}
protocol Reader {
typealias ReaderValueType: Value
func value() -> ReaderValueType
}
protocol Manager {
typealias ManagerValueType: Value
func read<ManagerReaderType: Reader where ManagerReaderType.ReaderValueType == ManagerValueType>() -> ManagerReaderType?
}
struct Value1: Value {
let value: Int = 1
}
struct Value2: Value {
let value: Int = 2
}
struct Reader1: Reader {
func value() -> Value1 {
return Value1()
}
}
struct Reader2: Reader {
func value() -> Value2 {
return Value2()
}
}
class Manager1: Manager {
typealias ManagerValueType = Value1
let v = ManagerValueType()
func read<ManagerReaderType: Reader where ManagerReaderType.ReaderValueType == ManagerValueType>() -> ManagerReaderType? {
return Reader1()// Error: "Reader1 is not convertible to ManagedReaderType?" Try swapping to return nil which does compile.
}
}
let manager = Manager1()
let v = manager.v.value
let a: Reader1? = manager.read()
a.dynamicType
The error occurs because ManagerReaderType in the read function is only a generic placeholder for any type which conforms to Reader and its ReaderValueType is equal to the one of ManagerReaderType. So the actual type of ManagerReaderType is not determined by the function itself, instead the type of the variable which gets assigned declares the type:
let manager = Manager1()
let reader1: Reader1? = manager.read() // ManagerReaderType is of type Reader1
let reader2: Reader2? = manager.read() // ManagerReaderType is of type Reader2
if you return nil it can be converted to any optional type so it always works.
As an alternative you can return a specific type of type Reader:
protocol Manager {
// this is similar to the Generator of a SequenceType which has the Element type
// but it constraints the ManagerReaderType to one specific Reader
typealias ManagerReaderType: Reader
func read() -> ManagerReaderType?
}
class Manager1: Manager {
func read() -> Reader1? {
return Reader1()
}
}
This is the best approach with protocols due to the lack of "true" generics (the following isn't supported (yet)):
// this would perfectly match your requirements
protocol Reader<T: Value> {
fun value() -> T
}
protocol Manager<T: Value> {
func read() -> Reader<T>?
}
class Manager1: Manager<Value1> {
func read() -> Reader<Value1>? {
return Reader1()
}
}
So the best workaround would be to make Reader a generic class and Reader1 and Reader2 subclass a specific generic type of it:
class Reader<T: Value> {
func value() -> T {
// or provide a dummy value
fatalError("implement me")
}
}
// a small change in the function signature
protocol Manager {
typealias ManagerValueType: Value
func read() -> Reader<ManagerValueType>?
}
class Reader1: Reader<Value1> {
override func value() -> Value1 {
return Value1()
}
}
class Reader2: Reader<Value2> {
override func value() -> Value2 {
return Value2()
}
}
class Manager1: Manager {
typealias ManagerValueType = Value1
func read() -> Reader<ManagerValueType>? {
return Reader1()
}
}
let manager = Manager1()
// you have to cast it, otherwise it is of type Reader<Value1>
let a: Reader1? = manager.read() as! Reader1?
This implementation should solve you problem, but the Readers are now reference types and a copy function should be considered.