How should I write this in Swift? - swift

Let's say that I create a protocol like this:
protocol A {
associatedtype T
func computeSomething(with:T) -> Double
}
In my generic typed class, I would like to do something like this:
class B<U> {
var doSomething:A<U>
}
This thing is that this generates an error, but I would like to accept any type that would support computeSomething on my type U but I don't know at all how to do that?
Edit for clarity
Basically if A was a generic struct or class, that would be possible, but what if no default implementation (provided by class or struct) makes sense here and the only thing I want is to ensure that the type does what I want?
Edit #2 (with concrete example)
I wanted to simplify my question which makes it pretty hard to understand so here is a still simplified and fictional problem that probably matches better the issue I am encountering:
I am writing a generic class that processes its generic type T:
class Process<T> { ... }
The class Process itself includes code that processes T, but in order for this code to work, it needs T to conform to some protocols, for instance:
protocol A {
func mixWith(other:A) -> A
}
protocol B {
var isFoo:Bool { get set }
}
So my first approach was to simply require T to conform to those protocols:
class Process<T:<A,B>> { ... }
This looks like the simplest approach and probably is in many cases, but in my case I think that this actually is problematic, for this reason:
First, I may need to process the same type in many different ways, and changing a way a type is being processed often requires changing the actual implementation of protocols A and B for instance in this case, fooProcess and barProcess are both of type Process with generic type MyType:
let fooProcess = Process<MyType>()
let barProcess = Process<MyType>()
But I want fooProcess and barProcess to do different operations which in many cases would require to change the implementation of the A and B protocols of my MyType type and that's simply not possible.
So my first idea was to simply require some closures and variables to be defined so that I wouldn't need protocols anymore and would define the way data is being processed only in my Process class, a little bit like this:
class Process<T> {
//
var mix:(_ lhs:T, _ rhs:T)->(T)
var isFoo:(_ a:T)->(Bool)
...
}
There all of the processing would be directly implemented in my Processing class, again this would have looked like the right solution but now comes another issue, which led me to my associated type approach: it turns out that in many cases, the user of my Process class would want to get some default behaviour implemented by my framework, for instance, I could automatically implement protocol A and B for them as long as their class conformed to protocol X, here is how it did it:
protocol X:A,B {
...
}
extension protocol X {
// Here was my default implementation of A and B, which enabled my user to directly get A and B implemented as long as their type conformed to X
}
By using this method, I would let my user directly choose what they wanted to implement themselves, by conforming to protocol X they would only need to write a little bit of code and let my framework to all of the rest by itself, and if they wanted to implement themselves A or B they still could.
So if I am right, there is no way to do such a thing with my closures implementation.
So for this reason, I thought that an associated type protocol would be a good solution because here I could let my users easily get some default behaviour or write their own, so now we are getting back to my original question:
protocol AProcessing {
associatedtype U
func mix(_ lhs:U, _ rhs:U) -> U
}
protocol BProcessing {
associatedtype U
func isFoo(_ a:U) -> Bool
}
And then do something like that:
class Process<T> {
var aProcessor:AProcessing<T>
var bProcessor:BProcessing<T>
}
Here the advantage compared to closures is that I could write a special class conforming to AProcessing that could provide default implementation, this way:
class AutomaticAProcessing<T:X>:AProcessing { ... }
That would have enabled my users to so something like that:
var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing<SomeType>()
processData.bProcessor = TheirOwnImplemtation
Not only is this not possible in Swift, but it also feels like I am using too many "hacks" to get things done and there should be an easier language feature to do that, unfortunately I don't know what I should use.

I don't think it is possible, because the generic type of the protocol is specified in the class that implements it.
You could write something like this:
class B<U, P: A> where P.T == U {
var someVar: P?
}
But then you would need to specify a second parameter with the specific class. For example:
class C: A {
typealias T = String
func computeSomething(with: String) -> Double {
return 0.0
}
}
let b = B<String, C>()
let c = b.someVar
But it can't return a protocol with specific type in its associatedtype

One way would be to start with an empty generic struct, and then extend it on types where it makes sense:
struct A<T> {}
extension A where T: Base {
func computeSomething(with: T) -> Double {
return 1
}
}
Usage:
protocol Base {}
class B<U: Base> {
let doSomething = A<U>()
func foo(x: U) -> Double {
return doSomething.computeSomething(with: x)
}
}
class Z : Base {}
let x = B<Z>()
let y = x.foo(x: Z())
print(y)

To EDIT #2:
Remove associated types, and it should be workable:
protocol A {}
protocol B {}
protocol AProcessing {
func mix(_ lhs: A, _ rhs: A) -> A
}
protocol BProcessing {
func isFoo(_ a: B) -> Bool
}
Then, your processor:
class Process<T: A & B> {
var aProcessor: AProcessing!
var bProcessor: BProcessing!
func foo(_ a: T) -> Bool {
let x = aProcessor.mix(a, a)
guard let b = x as? B else { return false }
return bProcessor.isFoo(b)
}
}
And usage:
struct AutomaticAProcessing : AProcessing {
func mix(_ lhs: A, _ rhs: A) -> A { return lhs }
}
struct TheirOwnImplemtation : BProcessing {
func isFoo(_ a: B) -> Bool { return false }
}
struct SomeType : A, B {}
var processData = Process<SomeType>()
processData.aProcessor = AutomaticAProcessing()
processData.bProcessor = TheirOwnImplemtation()
let x = SomeType()
let y = processData.foo(x)
print(y)

Related

Why can't I assign this protocol conforming class to a variable of the protocol type?

I've got a toy example here that I can't find any solutions for online.
protocol Tree {
associatedtype Element
// some nice tree functions
}
class BinaryTree<T> : Tree {
typealias Element = T
}
class RedBlackTree<T> : Tree {
typealias Element = T
}
enum TreeType {
case binaryTree
case redBlackTree
}
// Use a generic `F` because Tree has an associated type and it can no
// longer be referenced directly as a type. This is probably the source
// of the confusion.
class TreeManager<E, F:Tree> where F.Element == E {
let tree: F
init(use type: TreeType){
// Error, cannot assign value of type 'BinaryTree<E>' to type 'F'
switch(type){
case .binaryTree:
tree = BinaryTree<E>()
case .redBlackTree:
tree = RedBlackTree<E>()
}
}
}
I'm not sure what the problem here is or what I should be searching for in order to figure it out. I'm still thinking of protocols as I would interfaces in another language, and I view a BinaryTree as a valid implementation of a Tree, and the only constraint on F is that it must be a Tree. To confuse things even more, I'm not sure why the following snippet compiles given that the one above does not.
func weird<F:Tree>(_ f: F){ }
func test(){
// No error, BinaryTree can be assigned to an F:Tree
weird(BinaryTree<String>())
}
Any pointers or explanations would be greatly appreciated.
I don't understand the context of the situation this would be in. However, I have provided two solutions though:
1:
class Bar<F:Foo> {
let foo: FooClass.F
init(){
foo = FooClass.F()
}
}
2:
class Bar<F:Foo> {
let foo: FooClass
init(){
foo = FooClass()
}
}
What you are currently doing doesn't make logical sense, to whatever you are trying to achieve
I don't know what are you trying for, but of course it's not possible to do that. from your example, you attempt to create Bar class with generic. and that not the appropriate way to create a generic object, because the creation of generic object is to make the object to accept with any type.
Here's some brief explanation of the generic taken from Wikipedia.
On the first paragraph that says, "Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters."
it's very clear about what are the meaning of to-be-specified-later, right :)
Back to your example :
class Bar<F: Foo> {
let foo: F
init() {
// Error, cannot assign value of type 'FooClass' to type 'F'
foo = FooClass()
}
}
From the above code, it is type parameter F which has a constraint to a type Foo. and, you try to create an instance for foo variable with a concrete implementation, that is FooClass. and that's not possible since the foo variable is a F type(which is abstract). of course we can downcast it, like this foo = FooClass() as! F, but then the foo is only limited to FooClass, so why even bother with generic then ?
Hope it help :)
Your approach to this is wrong. In your original example, you would have to specify the type of both the element (E) and the container (BinaryTree or RedBlackTree), in addition to the enum value. That make no sense.
Instead, you should construct the manager to take a tree as the constructor argument, allowing Swift to infer the generic arguments, i.e.
class TreeManager<E, F: Tree> where F.Element == E {
var tree: F
init(with tree: F) {
self.tree = tree
}
}
let manager = TreeManager(with: BinaryTree<String>())
Alternatively, you should look into using opaque return types in Swift 5.1 depending on what the final goal is (the example here is obviously not a real world scenario)
Something like this seems reasonable. The point was to try and have some piece of logic that would determine when the TreeManager uses one type of Tree vs another.
protocol TreePicker {
associatedtype TreeType : Tree
func createTree() -> TreeType
}
struct SomeUseCaseA<T>: TreePicker {
typealias TreeType = RedBlackTree<T>
func createTree() -> TreeType {
return RedBlackTree<T>()
}
}
struct SomeUseCaseB<T>: TreePicker {
typealias TreeType = BinaryTree<T>
func createTree() -> TreeType {
return BinaryTree<T>()
}
}
class TreeManager<T, Picker: TreePicker> where Picker.TreeType == T {
let tree: T
init(use picker: Picker){
tree = picker.createTree()
}
This introduces another protocol that cares about picking the tree implementation and the where clause specifies that the Picker will return a tree of type T.
I think all of this was just the result of not being able to declare the tree of type Tree<T>. Its a bit more verbose but its basically what it would have had to look like with a generic interface instead. I think I also asked the question poorly. I should have just posted the version that didn't compile and asked for a solution instead of going one step further and confusing everyone.
protocol Foo {
associatedtype F
}
class FooClass : Foo {
typealias F = String
}
class Bar<M:Foo> {
let foo: M
init(){
foo = FooClass() as! M
}
}

protocol with same associated type name

If I have two protocols whose associated type happens to be the same, such as
protocol Read {
associatedtype Element
func read() -> Element
}
protocol Write {
associatedtype Element
func write(a: Element)
}
Then I would like to have a class to read integer from and write string to:
class ReadWrite: Read, Write {
func read() -> Int {
return 5
}
func write(a: String) {
print("writing \(a)")
}
}
but the compiler complains and suggests changing String to Int. Ideally the type should be inferred, or at least compiles if I explicitly declare
associatedtype Read.Element = Int
associatedtype Write.Element = String
within ReadWrite. Any work around?
update
Workaround inspired by this question is to create two auxiliary protocols
protocol ReadInt: Read {
associatedtype Element = Int
}
protocol WriteString: Write {
associatedtype Element = String
}
and have the class inherit from these two instead:
class ReadWrite: ReadInt, WriteString {
func read() -> Int {
return 5
}
func write(a: String) {
print("writing \(a)")
}
}
This seems to compile, but I am afraid of any gotcha following this way.
update again
I found the issue in Swift's issue tracker. Anyone require this missing feature (like me) should vote for it. As a comparison, this pattern is possible in Rust, which also supports associated types (although this is not an idiomatic usage).
Another workaround is to create a third, combined protocol:
protocol ReadWrite {
associatedtype R
associatedtype W
func read() -> R
func write(a: W)
}
It's not pretty, since it forces you to redeclare the protocol members, but it does keep it generic (you're not limited to String and Int).

Returning a generic from a protocol-defined function in Swift

I declared a protocol with a generic function, but it seems that the type inference isn't working properly after implementing it.
protocol SearchableRealmModel {
static func search<Self: Object>(needle: String) -> Results<Self>?
}
class Thing: Object, SearchableRealmModel {
class func search<Thing>(needle: String) -> Results<Thing>? {
return realm()?.objects(Thing).filter("name == '\(needle)'")
}
}
let things = Thing.search("hello") // works but inferred type is Results<Object>?
The problem here is that the inferred type of things is Results<Object>?. I realize these variations can be used,
let things: Results<Thing>? = Thing.search("hello")
let things = Thing.search("hello") as Results<Thing>?
but having to specify the type every time is quite repetitive.
In my tests, using other types than Results<..>? kept the type inference intact. And this could be caused by having to specify the parent class in Self: Object (which is required because of Results).
Any help is appreciated.
This is a limitation of Swift's generics machinery. The compiler can generate a concrete signature for static func search(needle: String) -> Results<Object>? which satisfies the type constraint because Object subclasses will match this. You could probably file a bug towards bugs.swift.org because I think the Swift core team would also consider this to be a bug, if not very unexpected behavior.
However, you can modify your code to use protocol extensions to do what you want:
protocol SearchableRealmModel {}
extension SearchableRealmModel where Self: Object {
static func search(needle: String) -> Results<Self> {
return try! Realm().objects(Self).filter("name == '\(needle)'")
}
}
class Thing: Object, SearchableRealmModel {
dynamic var name = ""
}
let result = Thing.search("thing1") // => inferred as Results<Thing>
print(result.first?.name)
If you want custom implementations of search for other Realm models, you can reimplement the function there, which the compiler will prioritize over the protocol extension version:
class OtherThing: Object, SearchableRealmModel {
dynamic var id = ""
static func search(needle: String) -> Results<OtherThing> {
return try! Realm().objects(OtherThing).filter("id == '\(needle)'")
}
}

inheritance and polymorphism in swift

I have the following problem:
Class A - super class.
Class A protocol:
has method ->
func test(params: GeneralParams, completionBlock: GeneralCompletionBlock)
GeneralParams is super class and has the following 2 subclasses: BParams, CParams.
now i have 2 more classes:
class B: A, A protocol
class C: A, A protocol
I want class B, C to use test function but with different class for their params for class B i want to use BParams and different completion block and for C the same thing. i want to have the same method for both with different parameters and implementation for both.
Whats the best solution for this situation?
Since Swift allows method overload, the best practice here would be to overload a method to suite your needs in a new class.
For instance:
class B: A{
func test(params: BParams, completionBlock: GeneralCompletionBlock){
...
}
}
class C: A{
func test(params: CParams, completionBlock: GeneralCompletionBlock){
...
}
}
I want class B, C to use test function but with different class for their params
The most important rule of subclassing is substitutability. If B is a kind of A, then everywhere an A can be used, it must be legal to use a B. So every subclass of A must support this method:
func test(params: GeneralParams, completionBlock: GeneralCompletionBlock)
It cannot restrict this method to other types (not even to subtypes of these). If it did, then there would be places that I could use A, but couldn't use B.
B is free to extend A and add new methods. So, as #tmac_balla suggests, you can add an overload that will be selected for subtypes. For example:
class AParam {}
class BParam: AParam {}
class A {
func f(param: AParam) {print("A")}
}
class B: A {
func f(param: BParam) {print("B")}
}
B().f(AParam()) // "A"
B().f(BParam()) // "B"
But B must still support being passed the superclass.
Most of the time in Swift, this is the wrong way to go about things. Subclassing introduces lots of complexity that you usually don't want. Instead you generally want protocols and generics in Swift. For example, rather than A, B, and C, you would just have a generic A and a protocol.
protocol Param {
var name: String { get }
}
struct AParam: Param {
let name = "A"
}
struct BParam: Param {
let name = "B"
}
struct S<P: Param> {
func f(param: P) {print(param.name)}
}
let a = S<AParam>()
a.f(AParam()) // "A"
// a.f(BParam()) // error; we've restricted the type it can take
let b = S<BParam>()
b.f(BParam()) // "B"

Typecasting and where in for loop

I have the following scenario:
protocol A {}
protocol B: A {}
protocol C: A {}
let objects: [A] = ...
How can I loop through the array and only execute logic for the objects that are of type B?
Right now, I'm doing something like this:
for object in objects {
if let b = object as? B {
...
}
}
But I was wondering if I can use where to make this more expressive and elegant.
for b in objects where b is B // <- compiles, but b is typed as A, not B
for b: B in objects where b is B // <- doesn't compile
for b in objects as! [B] where b is B // <- I get a warning that "is" will always be true
There is also for case (almost the same case as in switch statements) so it would look like this:
for case let b as B in objects {
// use b which is now of type B
}
Another nice expression is:
for case let b as protocol<B, C> in objects {
// use b which is now of type protocol<B, C>
}
so you can use methods, properties and so on from both protocols at the same time
as? subtype and its variants are a code smell. The other answers here will help you accomplish what you want, but I wanted to suggest that you move this logic from the for loop to the protocol (if it's possible).
For example, consider a Shape protocol:
protocol Shape {
func draw()
func executeSomeSpecialOperation()
}
extension Shape {
func executeSomeSpecialOperation() {
// do nothing by default
}
}
Create three shape types that conform to it:
struct Circle : Shape {
func draw() {
// drawing code goes here
}
}
struct Diamond : Shape {
func draw() {
// drawing code goes here
}
}
struct Pentagon : Shape {
func draw() {
// drawing code goes here
}
func executeSomeSpecialOperation() {
print("I'm a pentagon!")
}
}
As you know, you can create an array of shapes:
let shapes : [Shape] = [Circle(), Diamond(), Pentagon()]
This approach lets you loop through this array without knowing their type:
for shape in shapes {
shape.draw()
shape.executeSomeSpecialOperation()
}
This has two benefits:
Reduces coupling (your method running the for loop doesn't need to know what a Pentagon is)
Increases cohesion (logic related to Pentagon is contained within that type's definition)
I don't know for sure that this will work for your specific use case, but I think it's a better pattern generally.
I am not 100% certain that this answers your case - because you have something similar already - and I do not entirely understand what you do not like about your version. However this works in Swift 2:
for object in objectArray where object is protocolB {
//print(object) // only objects conforming to protocolB
}
Here are my declarations:
var objectArray: [AnyObject] = []
// contains a mix of objects of the following 3 classes
class class01: protocolA {
}
class class02: protocolA, protocolB {
func test() -> String {
// required to conform to protocolB
return "hello"
}
}
class class03: protocolA, protocolB, protocolC {
func test() -> String {
// required to conform to protocolB
return "hello"
}
}
protocol protocolA {
}
protocol protocolB: protocolA {
func test() -> String
}
protocol protocolC: protocolA {
}
compiles, but b is typed as A, not B
That's the bit I don't understand. Mostly likely because I am being dumb. But the way I am reading this, your protocolB objects also conform to protocolA as defined. I have defined mine the same.