Conforming to generic protocol method returning PATs - swift

I'd like to declare generic protocols similar to the following:
protocol Factory {
func createWidget<T, TWidget>(_ t: T) -> TWidget
where TWidget: Widget, TWidget.T == T
}
protocol Widget {
associatedtype T
func get() -> T
}
I'm hoping that I can implement concrete variations of Factory returning their own concrete and opaque Widget with a hidden implementation.
Here's an example implementation that fails to build:
struct ConcreteFactory: Factory {
func createWidget<T, TWidget>(_ t: T) -> TWidget
where TWidget: Widget, TWidget.T == T {
// This line has an error…
return ConcreteWidget(widgetValue: t)
}
}
struct ConcreteWidget<T>: Widget {
let widgetValue: T
init(widgetValue: T) {
self.widgetValue = widgetValue
}
func get() -> T {
return widgetValue
}
}
However, this does not compile.
At the line indicated, Swift's compiler give the error "Cannot convert return expression of type 'ConcreteWidget' to return type 'TWidget'".
I also tried having ConcreteFactory return a ConcreteWidget, but then the error is that ConcreteFactory doesn't conform to Factory.

This can’t work. When you call your createWidget method you specify two types T and TWidget.
struct MyWidget: Widget {
func get() -> Int { ... }
}
let widget: MyWidget = factory.createWidget(12)
In this Example TWidget is MyWidget and T is Int. And this shows nicely why your approach can’t work. You cannot assign a ConcreteWidget<Int> to a variable of type MyWidget.
What you need is a type-eraser for your widgets. Currently you have to write that yourself, but in the future the compiler will hopefully generate them automatically when needed.
struct AnyWidget<T>: Widget {
private let _get: () -> T
init<Other: Widget>(_ other: Other) where Other.T == T {
_get = other.get
}
func get() -> T {
return _get()
}
}
This allows you to write your factory protocol and implementation:
protocol Factory {
func createWidget<T>(_ t: T) -> AnyWidget<T>
}
struct ConcreteFactory: Factory {
func createWidget<T>(_ t: T) -> AnyWidget<T> {
return AnyWidget(ConcreteWidget(widgetValue: t))
}
}

Related

swift generic constraint type is also a return type

protocol Builder {
associatedtype Output where Output: MyProtocol
func build() -> Output?
}
// concrete type
struct ABuilder: Builder {
func builder() -> MyProtocol {
if someCondition {
return aSubClassOfMyProtocol
} else {
return anotherSubClassOfMyProtocol
}
}
}
MyProtocol is a protocol type. It is also the Output constraint. Because the concrete Builder ABuilder is going to return two different sub class types that conform MyProtocol. How can I make the generic constraint work?
I am trying to make the generic constraint be the same.
You can make build() function generic or use typecasting. Anyway this two ways determine concrete type outside of method not inside.
So first of all you need to remove associatedtype. If you specify associated type Output it means that the implementation of your Builder protocol should return some concrete type which conforms to MyProtocol. If you want your Builder to return any type which conforms to MyProtocol not the concrete one specified for this implementation of Builder then you shouldn't declare any associated types.
Let's see how type casting can work:
protocol Builder {
func build() -> MyProtocol?
}
struct ABuilder: Builder {
func build() -> MyProtocol? {
// some code
}
}
let builder = ABuilder()
guard let a = builder.build() as? SomeClass else { return }
Or we can use generic approach:
protocol Builder {
func build<T: MyProtocol>() -> T?
}
struct ABuilder: Builder {
func build<T: MyProtocol>() -> T? {
// some code
}
}
let builder = ABuilder()
let a: SomeClass = builder.build()
You can read more about that here, here and here. Moreover I strongly recommend watching this video which describes all the limitations of generics in swift.
I think you are correct that having the typecasting outside the Builder is a better way. I also found another possibility. The difference is that we have the concrete class types from the build function.
protocol MyProtocol { }
protocol Builder {
associatedtype Output
func build() -> Output?
}
struct ASubClassOfMyProtocol: MyProtocol {}
struct AnotherSubClassOfMyProtocol: MyProtocol {}
struct ABuilder: Builder {
func build() -> (any MyProtocol)? {
if Int.random(in: 0...10) < 5 {
return ASubClassOfMyProtocol()
} else {
return AnotherSubClassOfMyProtocol()
}
}
}

Use Type Erasure return Generic Type in a function with Swift (Cannot convert return expression of type…)

I have a problem with generics in swift. Let's expose my code.
protocol FooProtocol {
associatedtype T
}
protocol Fooable { }
extension Int : Fooable { }
extension String: Fooable { }
class AnyFoo<T>: FooProtocol {
init<P: FooProtocol>(p: P) where P.T == T { }
}
class FooIntImpClass: FooProtocol {
typealias T = Int
}
class FooStringImpClass: FooProtocol {
typealias T = String
}
func createOne(isInt: Bool) -> AnyFoo<Fooable> {
if isInt {
let anyFoo = AnyFoo(p: FooIntImpClass())
return anyFoo
} else {
let anyFoo = AnyFoo(p: FooStringImpClass())
return anyFoo
}
}
func createTwo<F: Fooable>(isInt: Bool) -> AnyFoo<F> {
if isInt {
let anyFoo = AnyFoo(p: FooIntImpClass())
return anyFoo
} else {
let anyFoo = AnyFoo(p: FooStringImpClass())
return anyFoo
}
}
createOne got an error
Cannot convert return expression of type 'AnyFoo' (aka 'AnyFoo') to return type 'AnyFoo'
createTwo got an error
Cannot convert return expression of type 'AnyFoo' (aka 'AnyFoo') to return type 'AnyFoo'
Why is this happening. I'm returning the correct value.
And What is the difference with the createOne and createTwo
EDIT to respond to the edit to the question:
createTwo doesn't work because you have the same misconception as I said in my original answer. createTwo decided on its own that F should be either String or Int, rather than "any type that conforms to Fooable".
For createOne, you have another common misconception. Generic classes are invariant. AnyFoo<String> is not a kind of AnyFoo<Fooable>. In fact, they are totally unrelated types! See here for more details.
Basically, what you are trying to do violates type safety, and you redesign your APIs and pick another different approach.
Original answer (for initial revision of question)
You seem to be having a common misconception of generics. Generic parameters are decided by the caller, not the callee.
In createOne, you are returning anyFoo, which is of type AnyFoo<Int>, not AnyFoo<P>. The method (callee) have decided, on its own, that P should be Int. This shouldn't happen, because the caller decides what generic parameters should be. If the callee is generic, it must be able to work with any type (within constraints). Anyway, P can't be Int here anyway, since P: FooProtocol.
Your createOne method should not be generic at all, as it only works with Int:
func createOne() -> AnyFoo<Int> {
let anyFoo = AnyFoo(p: FooImpClass())
return anyFoo
}
Is the following what you tried to achieve? (compiled & tested with Xcode 11.4)
func createOne() -> some FooProtocol {
let anyFoo = AnyFoo(p: FooImpClass())
return anyFoo
}
EDIT
I finally managed to keep your where clause :)
EDIT
Still not sure what you want to do, and I still agree with #Sweeper but I love to badly abuse generics :) :
protocol FooProtocol {
associatedtype T
init()
}
protocol Fooable { }
extension Int : Fooable { }
extension String: Fooable { }
class AnyFoo<T>: FooProtocol {
init<P: FooProtocol>(p: P) where P.T == T { }
init<T>(p: T.Type) { }
required init() { }
}
class FooIntImpClass: FooProtocol {
typealias T = Int
required init() { }
}
class FooStringImpClass: FooProtocol {
typealias T = String
required init() { }
}
func createOne<F: FooProtocol>(foo: F.Type) -> AnyFoo<F.T> {
let anyFoo = AnyFoo<F.T>(p: F.init())
return anyFoo
}
func createTwo<F: FooProtocol>(foo: F.Type) -> some FooProtocol {
let anyFoo = AnyFoo<F.T>(pk: F.T.self)
return anyFoo
}
that compiles but I don't know what to do with it.
Edit
yeah I really don't know:
let one = createOne(foo: FooStringImpClass.self) // AnyFoo<String>
print(type(of: one).T) // "String\n"
let two = createTwo(foo: FooIntImpClass.self) // AnyFoo<Int>
print(type(of: two).T) // "Int\n"
Is that what you wanted ?
I'm not sure what you want to do with that, but I suggest you try putting a where clause on the AnyFoo class instead of it's initializer.
And I should add, that your where clause on the initializer was wrong, Like Sweeper said :
Anyway, P can't be Int here anyway, since P: FooProtocol.
The following code compiles :
protocol FooProtocol {
associatedtype T
}
class AnyFoo<T>: FooProtocol where T: FooProtocol {
init<P: FooProtocol>(p: P) { }
}
class FooImpClass: FooProtocol {
typealias T = Int
}
func createOne<P: FooProtocol>() -> AnyFoo<P> {
let anyFoo: AnyFoo<P> = AnyFoo(p: FooImpClass())
return anyFoo
}

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

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 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.