Workaround for conditional conformance in Swift (was: Adding protocol to a constrained generic types) - swift

The code snippet below demonstrates the problem I am trying to solve.
import Foundation
protocol Printable {
func className() -> String
}
class SomeType: Printable {
func className() -> String {
return "SomeType"
}
}
class List<T> {
}
extension List where T: SomeType {
func className() -> String {
return "List<SomeType>"
}
}
func test(type: Any, message: String) {
guard type is Printable else {
print("\(message): ERROR")
return
}
print("\(message): SUCCESS")
}
let s: Any = SomeType()
test(type: s, message: "#1")
let slist1: Any = List<Any>()
test(type: slist1, message: "#2")
let slist2: Any = List<SomeType>()
test(type: slist2, message: "#3")
How can I get this:
> #1: SUCCESS <--- as expected
> #2: ERROR <--- it's okay
> #3: SUCCESS <--- I am getting ERROR instead
It seems that adding a protocol to this line would do the trick:
extension List: Printable where T: SomeType { // COMPILE ERROR
But unfortunately, this is not allowed.
Another way to do it could be to use:
extension List where T: Printable { // COMPILES OK in Swift 2.3 but doesn't work. COMPILE ERROR in Swift 3.0
But again, no luck passing the test.
What else can I do to add a protocol to a constrained generic type?

An updated answer now that we have Swift 4.2, conditional conformance is now added as a feature.
From their spec on Github, this sort of code is now valid
extension List : Printable where T: SomeType {
func className() -> String {
return "List<SomeType>"
}
}

Okay so in your guard you're asking "if this is Printable then print success else print Error" and with your first example you have s which is SomeType which is printable. That's fine.
After that you have slist1 which is type List<Any> which is definitely not of type printable and you get "Error". That's fine
Next you have List<SomeType>. Now you have a class extension that defines T to be SomeType, correct? But you're only defining T to be SomeType and not the actual List so when you pass the entire List into the test function you're not going to get your test to pass because List<AnyTypeHere> is not Printable because the list itself doesn't implement Printable.
Now the question is, do you want the entire list to be printable? If so, then just make it conform to the SomeType or Printable protocol. That's the only way you'll get that to pass other than you passing individual List<SomeType> elements into the function. Your function logic is correct but it's just a misuse of the concept.
So if you want the List<SomeType> to make that pass then you could do something like
class List<T> : Printable where T:SomeType {
//Add code here that conforms to protocol
}
Doing that will make your second test fail because Any doesn't inherit from SomeType but it'll make your third test pass because now List<T> is Printable and T is also of type SomeType. I mean, that's just a real quick way to get what it looked like you wanted to begin with. You're not going to have the second and third tests pass at the same time unless you add something extra because the second test is List being of type Any while the third is List being of type Printable. So either one of them will throw an error (because List isn't of type Printable) or all tests show success (because List is of type Printable)

Related

Swift - Protocol default implementation in extension with generic superclass constraint

I am trying to constraint a protocol extension to a generic class. My goal is to provide a default implementation of a protocol where Self is any instance that is subclass of some generic class. Consider the below example:
protocol Printable {
var value: String { get }
}
class Printer<P: Printable> {
let printable: P
init(printable: P) {
self.printable = printable
}
}
protocol Press {
func print()
}
// error: reference to generic type 'Printer' requires arguments in <...>
extension Press where Self: Printer {
func print() {
// Do Something with self.printable.value
}
}
The compiler gives error error: reference to generic type 'Printer' requires arguments in <...>.
I don't understand why this should not be allowed. As long as Press is some kind of Printer which always works with some kind of Printable things should work, right? Or I am missing something?
Can you point what could be the right way of achieving something like this?
This is because Printer<A> and Printer<B> are different types, even A & B are Printable, so due to possible ambiguity compiler generate error.
You need the following (tested with Xcode 11.4)
extension Press {
func print<P>() where Self: Printer<P>, P: Printable {
// Do Something with self.printable.value
}
}

How can I use a protocol's type in method signature?

I have a function like this:
// A generic function that can fetch values from the store given a type
// Corresponds to a table and it's rows
func fetch<T: FetchableRecord>(for theType: T.Type) -> [T] {
// ... fetch from a store using the type
// This compiles fine
}
How can I use this with a collection of types?
Ideally, if I have some conformers:
struct ModelA: FetchableRecord { }
struct ModelB: FetchableRecord { }
then I would like to be able to do:
let modelTypes: [FetchableRecord.Type] = [ModelA.self, ModelB.self]
modelTypes.forEach {
fetch(for: $0) // xxx: does not compile: "Cannot invoke 'fetch' with an argument list of type '(for: FetchableRecord.Type)'"
}
At the very least, I would like to figure why this would not be possible.
Thank you.
The reason for the error is FetchableRecord.Type is not the same as ModelA.Type or ModelB.Type. Even if both of the structs conform to FetchableRecord protocol, constraining the models (by conforming to a certain protocol) does not affect the "Types", more technically speaking:
ModelA.self == FetchableRecord.self OR ModelB.self == FetchableRecord.self is false.
In order to resolve this issue, you could implement the method's signiture as:
func fetch(for theType: FetchableRecord.Type) -> [FetchableRecord] {
// ...
}
therefore, your code:
let modelTypes: [FetchableRecord.Type] = [ModelA.self, ModelB.self]
modelTypes.forEach {
fetch(for: $0)
}
should work. At this point, you are dealing with it "Heterogeneously" instead of "Homogeneously".
Furthermore, if that makes it more sensible, note that when calling fetch method as per your implementation, it is:
the parameter type is FetchableRecord.Protocol. However, as per the above-mentioned implementation (in this answer), it is:
the parameter type is FetchableRecord.Type, which is the wanted result.

How to iterate over an array of types in Swift 4.1?

I'm using Xcode 9.4.1 with Swift 4.1 on iOS 11.4.1.
I've got a handful of protocols, like this:
protocol Bla {
// some stuff
}
protocol Gorp {
// some other stuff
}
And I've got some structs that conform to both of those protocols, like this:
typealias MyType = Bla & Gorp
struct Hello: MyType {
var ID: Int
var name: String
}
struct Goodbye: MyType {
var percentage: Double
var hairbrush: String
}
Then I've got a func that takes an argument, theType, which conforms to both Bla & Gorp. In this example, I'm just printing a description -- like this:
func doSomething<T: MyType>(_ theType: T.Type) {
// some stuff
print("Got called... \(theType)")
}
And, I can call this function passing each of the two struct types (Hello and Goodbye), like this:
doSomething(Hello.self)
doSomething(Goodbye.self)
This works great and I get the following output, as expected:
Got called... Hello
Got called... Goodbye
However, what I'd really like to do is to iterate over a bunch of these, rather than calling them individually.
This way gives me an error "note: expected an argument list of type '(T.Type)'":
for thingy in [Hello.self, Goodbye.self] {
doSomething(thingy)
}
If I add an as! [MyType.Type] or as! [MyType], I get the same error. I've also tried this:
for thingy in [Hello.self as MyType.Type, Goodbye.self as MyType.Type] {
doSomething(thingy)
}
Same error as the rest.
I've also tried without the typealias.
If I start typing, the autocomplete says that doSomething is expecting an argument of type (Bla & Gorp).Protocol. So, I also tried this:
for thingy in [Hello.self, Goodbye.self] as! [(Bla & Gorp).Protocol] {
doSomething(thingy)
}
In this case, I get the message:
In argument type '(Bla & Gorp).Protocol', 'Bla & Gorp' does not conform to expected type 'Bla'
Also tried this sort of thing, which gave an error, "Cannot invoke 'doSomething' with an argument list of type '(MyType.Type)'":
struct AnyBlaGorp {
let blaGorp: MyType.Type
init<T: MyType>(_ theType: T.Type) {
self.blaGorp = theType
}
}
for thingy in [AnyBlaGorp(Hello.self), AnyBlaGorp(Goodbye.self)] {
doSomething(thingy.blaGorp)
}
Pointers to the magical correct syntax would be greatly appreciated. :)
You can make the doSomething method non-generic and accept a MyType.Type. You can only do this if your protocols don't have Self or associated types.
func doSomething(_ theType: MyType.Type) {
// some stuff
print("Got called... \(theType)")
}
Next, you cast the array to a [MyType.Type]:
for thingy in [Hello.self, Goodbye.self] as [MyType.Type] {
doSomething(thingy)
}
This makes use of the fact that A.self can be converted to the type B.Type if A inherits from/conforms to B.

Swift: casting un-constrained generic type to generic type that confirms to Decodable

Situation
I have a two generic classes which will fetch data either from api and database, lets say APIDataSource<I, O> and DBDataSource<I, O> respectively
I will inject any of two class in view-model when creating it and view-model will use that class to fetch data it needed. I want view-model to work exactly same with both class. So I don't want different generic constraints for the classes
// sudo code
ViewModel(APIDataSource <InputModel, ResponseModel>(...))
// I want to change the datasource in future like
ViewModel(DBDataSource <InputModel, ResponseModel>(...))
To fetch data from api ResponseModel need to confirms to "Decodable" because I want to create that object from JSON. To fetch data from realm database it need to inherit from Object
Inside ViewModel I want to get response like
// sudo code
self.dataSource.request("param1", "param2")
If developer tries to fetch api data from database or vice-versa it will check for correct type and throws proper error.
Stripped out version of code for playground
Following is stripped out version of code which shows what I want to achieve or where I am stuck (casting un-constrained generic type to generic type that confirms to Decodable)
import Foundation
// Just to test functions below
class DummyModel: Decodable {
}
// Stripped out version of function which will convert json to object of type T
func decode<T:Decodable>(_ type: T.Type){
print(type)
}
// This doesn't give compilation error
// Ignore the inp
func testDecode<T:Decodable> (_ inp: T) {
decode(T.self)
}
// This gives compilation error
// Ignore the inp
func testDecode2<T>(_ inp: T){
if(T.self is Decodable){
// ??????????
// How can we cast T at runtime after checking T confirms to Decodable??
decode(T.self as! Decodable.Type)
}
}
testDecode(DummyModel())
Any help or explanation that this could not work would be appreciated. Thanks in advance :)
As #matt suggests, moving my various comments over to an answer in the form "your problem has no good solution and you need to redesign your problem."
What you're trying to do is at best fragile, and at worst impossible. Matt's approach is a good solution when you're trying to improve performance, but it breaks in surprising ways if it impacts behavior. For example:
protocol P {}
func doSomething<T>(x: T) -> String {
if x is P {
return "\(x) simple, but it's really P"
}
return "\(x) simple"
}
func doSomething<T: P>(x: T) -> String {
return "\(x) is P"
}
struct S: P {}
doSomething(x: S()) // S() is P
So that works just like we expect. But we can lose the type information this way:
func wrapper<T>(x: T) -> String {
return doSomething(x: x)
}
wrapper(x: S()) // S() simple, but it's really P!
So you can't solve this with generics.
Going back to your approach, which at least has the possibility of being robust, it's still not going to work. Swift's type system just doesn't have a way to express what you're trying to say. But I don't think you should be trying to say this anyway.
In the method that fetch data I will check type of generic type and if it confirms to "Decodable" protocol I will use it to fetch data from api else from database.
If fetching from the API vs the database represents different semantics (rather than just a performance improvement), this is very dangerous even if you could get it to work. Any part of the program can attach Decodable to any type. It can even be done in a separate module. Adding protocol conformance should never change the semantics (outwardly visible behaviors) of the program, only the performance or capabilities.
I have a generic class which will fetch data either from api or database
Perfect. If you already have a class, class inheritance makes a lot of sense here. I might build it like:
class Model {
required init(identifier: String) {}
}
class DatabaseModel {
required init(fromDatabaseWithIdentifier: String) {}
convenience init(identifier: String) { self.init(fromDatabaseWithIdentifier: identifier )}
}
class APIModel {
required init(fromAPIWithIdentifier: String) {}
convenience init(identifier: String) { self.init(fromAPIWithIdentifier: identifier )}
}
class SomeModel: DatabaseModel {
required init(fromDatabaseWithIdentifier identifier: String) {
super.init(fromDatabaseWithIdentifier: identifier)
}
}
Depending on your exact needs, you might rearrange this (and a protocol might also be workable here). But the key point is that the model knows how to fetch itself. That makes it easy to use Decodable inside the class (since it can easily use type(of: self) as the parameter).
Your needs may be different, and if you'll describe them a bit better maybe we'll come to a better solution. But it should not be based on whether something merely conforms to a protocol. In most cases that will be impossible, and if you get it working it will be fragile.
What you'd really like to do here is have two versions of testDecode, one for when T conforms to Decodable, the other for when it doesn't. You would thus overload the function testDecode so that the right one is called depending on the type of T.
Unfortunately, you can't do that, because you can't do a function overload that depends on the resolution of a generic type. But you can work around this by boxing the function inside a generic type, because you can extend the type conditionally.
Thus, just to show the architecture:
protocol P{}
struct Box<T> {
func f() {
print("it doesn't conform to P")
}
}
extension Box where T : P {
func f() {
print("it conforms to P")
}
}
struct S1:P {}
struct S2 {}
let b1 = Box<S1>()
b1.f() // "it conforms to P"
let b2 = Box<S2>()
b2.f() // "it doesn't conform to P"
This proves that the right version of f is being called, depending on whether the type that resolves the generic conforms to the protocol or not.

Can I constrain a generic parameter to *not* be optional?

Let's say I have this code:
func hello<T>(thing: T) -> String {
return "hello \(thing)"
}
Can I write a version of the hello function that won't compile if it's passed an optional?
let foo = "some"
let bar: String? = nil
print(helloNoOptional(foo)) // should compile
print(helloNoOptional(bar)) // should not compile
I'm thinking maybe it's doable with a protocol conformance or where clause on T but I can't think of how exactly that would work.
The reason I want to do this is because I'm dealing with a actual function in legacy codebase that has no sensible behavior if thing is nil. So I'd rather prevent hello from being called on an optional rather than deal with unwrapping thing inside of hello and trying to figure out a sensible error behavior.
Update:
A possible path...I realized that the Optional enum conforms to the NilLiteralConvertible protocol. So if I can find a way to constrain my generic to not conform to a type, I can exclude optionals de facto. But I don't know if it's possible to do something like
<T where !T: NilLiteralConvertible>
Best I can think of is overload and check at runtime:
func hello<T>(thing: T) -> String {
return "hello \(thing)"
}
fun hello<T>(thing: T?) -> String {
fatalError("No optionals allowed!")
}
hello("swift") // fine
hello(2) // fine
hello(Int("2")) // fatal error
But I don't know of a way of generating a compile-time error instead.
Edited
You can create a dummy protocol (NotOfOptionalType below) and extend all types you expect to use in your generic functions by this protocol. Finally use the dummy protocol as a type constraint for the parameter(s) in your generic functions; optionals does not meet this type constraint and you'll be given an error at compile time if they are sent as parameters for these functions.
// dummy protocol
protocol NotOfOptionalType {}
extension String : NotOfOptionalType {}
extension Int : NotOfOptionalType {}
extension Double : NotOfOptionalType {}
// ... extend to the types you will use
func hello<T: NotOfOptionalType > (thing: T) -> String {
return "hello \(thing)"
}
let foo = "some"
var bar: String? = nil
print(hello(foo)) // compiles
print(hello(bar)) // fails at compile time
bar = "something"
print(hello(bar)) // fails at compile time
print(hello(bar!)) // compiles
Building on #Airspeed Velocity's answer, I personally prefer to keep everything in one place, like so...
func hello<T>(_ thing: T) -> String {
if T.self is ExpressibleByNilLiteral.Type {
fatalError("Optional types not supported")
}
return "hello \(thing)"
}