I am trying to convert a design of mine to be pop. However I am stuck and already have bunch of threads for my approaches -not a duplicate of this question though- and apparently they are all dead end.
My question is, is there a way to override parameter types of a protocol method which inherited from another protocol?
struct Books: Codable {}
protocol Listener {
func listen(_ param: Codable)
}
protocol BooksListener: Listener {
func listen(_ param: Books)
}
class MyClass: BooksListener {
// I want only this one to required with the type.
func listen(_ param: Books) {
<#code#>
}
func listen(_ param: Codable) {
<#code#>
}
}
I did my research and I believe this is not how protocols work. I am just seeking a solution to this.
I tried to add a associatedType to the Listener and use it as the type inside listen(_:). But this solution restricts any class to have conformance to multiple protocols which inherits from Listener. Details can be found here
As a general rule, Swift protocols are not covariant. But your question is a special case, where, even if Swift allowed protocol covariance, your approach couldn't work.
In your example, a BooksListener is required to do everything a Listener can do. A Listener can take an arbitrary Codable as its parameter to listen. That means that a BooksListener must also be able to take an arbitrary Codable. Consider the following:
struct Books: Codable {}
protocol Listener {
func listen(_ param: Codable)
}
protocol BooksListener: Listener {
func listen(_ param: Books)
}
class MyClass: BooksListener {
// Illegal, but assuming you had what you're asking for
func listen(_ param: Books) {}
}
let l: Listener = MyClass()
l.listen("Strings are Codable, and Listener accepts Codable")
What would this do? This is violating LSP.
What you might want here is an associatedtype as you mention (but I'll explain later why you probably don't). With an associatedtype, you can define Listener the way I expect you mean.
struct Books: Codable {}
protocol Listener {
associatedtype Parameter: Codable
func listen(_ param: Parameter)
}
protocol BooksListener: Listener where Parameter == Books {
func listen(_ param: Books)
}
class MyClass: BooksListener {
func listen(_ param: Books) {}
}
Why don't you want this? Because it almost certainly doesn't mean anything. What can I possibly do with an arbitrary Listener? I can't call listen on it; I don't know the type of Parameter. There's no algorithm that it helps. And enabling algorithms is what associatedtypes are about.
But the real point is that "POP" doesn't mean "use lots of protocols" and absolutely doesn't mean "recreate class inheritance with protocols." It means "start with concrete types and extract protocols to share algorithms." So before you create a single protocol, you need to ask, what other kinds of Listeners exist in your program, and what algorithm needs to work on an arbitrary Listener? If there's no such algorithm, there should be no protocol.
Related
I am thinking on this for hours:
Lets say you have a generic game. The game can have players and an methode that sets a score. This can be done by using a protocol:
protocol Game {
var players: [Player] {get set}
func setScore (for player: Player, value: Int)
func sendSomeMessage(message: String)
}
i can now create a struct confirming to the protocol "Game"
struct raiseGame: Game {
var players: [Player]()
func setScore (for player: Player, value: Int) {
player.score += value
}
func sendSomeMessage(message: String) {
players.forEach { player in
//send the message to each player
}
}
}
But then i have, lets say, another struct conforming to Game. But in this time the score should be decreased. All other things stay the same. Now i have to write all the code for the func sendSomeMessage again. (There could be many more functions which stays the same).
The question is now: Should i switch to a class, write a base class and inherit from it, or is there a way to provide somekind of default implementations for functions in protocols, so i don't need to write them every time again, when confirming to a protocol?
is there a way to provide somekind of default implementations for functions in protocols, so i don't need to write them every time again, when confirming to a protocol?
There is indeed a way to provide default method implementations for protocol-conforming types:
protocol MyProtocol {
func myMethodRequirement()
}
extension MyProtocol {
func myMethodRequirement() {
// Default implementation.
}
}
struct Foo: MyProtocol {
func myMethodRequirement() {
// Foo-specific implementation.
}
}
struct Bar: MyProtocol {
/* Inherits default implementation instead. */
}
In your case, giving sendSomeMessage(message:) a default implementation would look like:
extension Game {
func sendSomeMessage(message: String) {
// Default implementation here.
}
}
Any type conforming to Game could either implement sendSomeMessage(message:) itself (similar to override-ing a method in a class), or use the default implementation without doing any additional work.
For more information, you can see the "Providing Default Implementations" section on protocols of the The Swift Programming Language guide.
To answer the title of your question, then, given that it is possible to do this for structs:
A class may be useful over a struct if you have an inheritance hierarchy multiple levels deep, which may be more complicated to express using protocol conformances
A class may be useful over a struct when you need reference semantics — see:
Structures and Enumerations are Value Types and
Classes are Reference Types
In your specific case, it doesn't immediately appear that either of these apply, but with more information, it might become obvious whether switching would be beneficial or not.
Suppose I have this:
protocol MyStuff: Hashable {
var stuff: String { get }
}
extension MyStuff {
func hash(into hasher: inout Hasher) {
hasher.combine(stuff)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.stuff == rhs.stuff
}
}
struct Stuff: MyStuff {
let stuff: String
}
Where I have a protocol which conforms to Hashable, and I extend that protocol to implement the requirements of Hashable. This works great.
I'm now trying to use the api like this:
let set: Set<MyStuff> = [Stuff(stuff: "Stuff")]
However I get this error:
Protocol 'MyStuff' as a type cannot conform to 'Hashable'
This question is actually a follow up to this answer written by George, and in that answer it says that
I believe SE-0309 (Unlock existentials for all protocols) could fix this.
I was wondering if that (the above link) would actually apply into this situation (listed in the code block above), as I'm having trouble fully understanding the proposal.
There are a couple of issues here, not necessarily due to the code you wrote, but due to how Swift is currently designed:
Protocols don't conform to other protocols, which means that MyStuff is not a sub-type of Hashable, which means you cannot use it as argument for the generic Set
Protocols with associated types, or self requirements, like Equatable, from which Hashable derives, can't be used as generic arguments, can only be used as generic constraints
The compiler runs into issue #1 from above, and that one gives the (clear maybe) message that protocols as types cannot be used as generic arguments instead of other protocols.
SE-0309 might solve issue #2, however you're stuck on #1, so you'll have to change your design.
Solutions? As others have suggested in the comments:
use a type eraser (e.g. https://stackoverflow.com/a/64476569/1974224)
use classes, and replace the protocol by a base class
I want to know why my SomeResourceRepository is still generic, even though it is only defined in one case only, which is when I set ResourceType = SomeResource, which XCode formats as below with the where clause. Code below which shows the exact setup I'm trying to achieve, written in a Playground.
I am trying to define a generic protocol for any given ResourceType such that the ResourceTypeRepository protocol then automatically requires the same set of functions, without having to copy-paste most of GenericRepository only to manually fill in the ResourceType for each Repository I make. The reason I need this as a protocol is because I want to be able to mock this for testing purposes later. So I'll provide an implementation of said protocol somewhere else in the actual app.
My interpretation of the code below is that it should work, because both SomeResourceLocalRepository and SomeResourceRemoteRepository are concrete, as I have eliminated the associated type by defining them "on top of" SomeResourceRepository, which is only defined where ResourceType == SomeResource.
import Foundation
struct SomeResource: Identifiable {
let id: String
let name: String
}
struct WhateverResource: Identifiable {
let id: UUID
let count: UInt
}
protocol GenericRepository: class where ResourceType: Identifiable {
associatedtype ResourceType
func index() -> Array<ResourceType>
func show(id: ResourceType.ID) -> ResourceType?
func update(resource: ResourceType)
func delete(id: ResourceType.ID)
}
protocol SomeResourceRepository: GenericRepository where ResourceType == SomeResource {}
protocol SomeResourceLocalRepository: SomeResourceRepository {}
protocol SomeResourceRemoteRepository: SomeResourceRepository {}
class SomeResourceLocalRepositoryImplementation: SomeResourceLocalRepository {
func index() -> Array<SomeResource> {
return []
}
func show(id: String) -> SomeResource? {
return nil
}
func update(resource: SomeResource) {
}
func delete(id: String) {
}
}
class SomeResourceService {
let local: SomeResourceLocalRepository
init(local: SomeResourceLocalRepository) {
self.local = local
}
}
// Some Dip code somewhere
// container.register(.singleton) { SomeResourceLocalRepositoryImplementation() as SomeResourceLocalRepository }
Errors:
error: Generic Protocols.xcplaygroundpage:45:16: error: protocol 'SomeResourceLocalRepository' can only be used as a generic constraint because it has Self or associated type requirements
let local: SomeResourceLocalRepository
^
error: Generic Protocols.xcplaygroundpage:47:17: error: protocol 'SomeResourceLocalRepository' can only be used as a generic constraint because it has Self or associated type requirements
init(local: SomeResourceLocalRepository) {
I will probably have to find another way to accomplish this, but it is tedious and quite annoying as we will produce a lot of duplicate code, and when we decide to change the API of our repositories, we will have to manually change it for all the protocols as we don't follow a generic "parent" protocol in this work-around.
I have read How to pass protocol with associated type as parameter in Swift and the related question found in an answer to this question, as well as Specializing Generic Protocol and others.
I feel like this should work, but it does not. The end goal is a concrete protocol that can be used for dependency injection, e.g. container.register(.singleton) { ProtocolImplementation() as Protocol } as per Dip - A simple Dependency Injection Container, BUT without copy-pasting when the protocol's interface clearly can be made generic, like in the above.
As swift provides a way to declare generic protocols (using associatedtype keyword) it's impossible to declare a generic protocol property without another generic constraint. So the easiest way would be to declare resource service class generic - class SomeResourceService<Repository: GenericRepository>.
But this solution has a big downside - you need to constraint generics everywhere this service would be involved.
You can drop generic constraint from the service declaration by declaring local as a concrete generic type. But how to transit from generic protocol to the concrete generic class?
There's a way. You can define a wrapper generic class which conforms to GenericRepository. It does not really implements its methods but rather passes to an object (which is real GenericRepository) it wraps.
class AnyGenericRepository<ResourceType: Identifiable>: GenericRepository {
// any usage of GenericRepository must be a generic argument
init<Base: GenericRepository>(_ base: Base) where Base.ResourceType == ResourceType {
// we cannot store Base as a class property without putting it in generics list
// but we can store closures instead
indexGetter = { base.index() }
// and same for other methods or properties
// if GenericRepository contained a generic method it would be impossible to make
}
private let indexGetter: () -> [ResourceType] {
indexGetter()
}
// ... other GenericRepository methods
}
So now we have a concrete type which wraps real GenericRepository. You can adopt it in SomeResourceService without any alarm.
class SomeResourceService {
let local: AnyGenericRepository<SomeResource>
}
I want to create a protocol which is only adopted by a specific class and its subClassses in swift.
I know i can use protocol extensions like this
protocol PeopleProtocol: class {
}
extension PeopleProtocol where Self: People {
}
But the method that will go in my protocol will be an init method which will be implemented by a class or its subClasess and will return only some specific type of objects.
some thing like this.
protocol PeopleProtocol: class {
init() -> People
}
or i can do some thing like this
extension PeopleProtocol where Self : People {
init()
}
But there are two problems,
In the first approach if i put an init method in the protocol it don't allow me to put a return statement there like -> People in the first approach.
In the second approach i have to provide a function body in the protocol extensions, so this thing will be out of question, as i don't know what specific type to return for this general implementation.
So any suggestions how i can call an init method and do either:
Let the protocol (not protocol extension) to be implemented by only specific classe and its subClasses.
Or return an instance of a certain from protocol extension method without giving its body.
You could add a required method that you only extend for the appropriate classes.
for example:
protocol PeopleProtocol
{
var conformsToPeopleProtocol:Bool { get }
}
extension PeopleProtocol where Self:People
{
var conformsToPeopleProtocol:Bool {return true}
}
class People
{}
class Neighbours:People
{}
extension Neighbours:PeopleProtocol // this works
{}
class Doctors:People,PeopleProtocol // this also works
{}
class Dogs:PeopleProtocol // this will not compile
{}
This could easily be circumvented by a programmer who would want to, but at least it will let the compiler warn you if you try to apply the protocol to other classes.
I have a protocol that has a static method with a default parameter. I want to change the default value in a class that implements the protocol. Essentially doing what is easily done with classes and super.
I only have a solution when the Protocol has no associated type.
The following code works, but as soon as you uncomment the associated type declaration, it doesn't compile.
protocol Protocol {
// associatedtype AssociatedType
}
extension Protocol {
func sayHello(name: String = "World") {
print("Hello, \(name)!")
}
}
class Class<T>: Protocol {
typealias AssociatedType = T
func sayHello(name: String = "Stack Overflow") {
// Uncommenting the Protocol.AssociatedType causes:
// Protocol can only be used as a generic constraint because it has associated type requirements
(self as Protocol).sayHello(name)
}
}
Class<()>().sayHello()
I do understand why it doesn't compile: Protocol has no concrete type for AssociatedType.
So maybe the question should read "Can I explicitly specialize a protocol?", to which I believe the answer is no.
I have a partial workaround. But even when it works, it sucks.
Especially when you consider that I'm writing a library where sayHello is public, so the following workaround forces me to have a second protocol, which has to be public, but is useless.
Here's the workaround:
protocol Parent {}
protocol Protocol: Parent {
associatedtype AssociatedType
}
extension Parent {
func sayHello(name: String = "World") {
print("Hello, \(name)!")
}
}
class Class<T>: Protocol {
typealias AssociatedType = T
func sayHello(name: String = "Stack Overflow") {
(self as Parent).sayHello(name)
}
}
Class<()>().sayHello()
But this doesn't work for me, because my sayHello uses the associated type. So it can't be extracted to another protocol.
Just to be sure I'm clear, here's what I'd like, only substituting the class for a protocol:
class Protocol<T> {
func sayHello(name: String = "World") {
print("Hello, \(name)!")
}
}
class Class<T>: Protocol<T> {
override func sayHello(name: String = "Stack Overflow") {
super.sayHello(name)
}
}
Class<()>().sayHello()
You're trying to reinvent inheritance in protocols, and there is no such thing. But it is trivial to get what you're talking about; just say what you mean. You don't mean "I want to do the thing I inherited." You mean "I want to do some common behavior." Just provide a name for that common behavior. This removes all ambiguity about which one you mean.
protocol Protocol {
associatedtype AssociatedType
}
extension Protocol {
// Put the default behavior on the protocol, not on the instance
// Of course you could also put it on the instance if that were convenient.
static func defaultSayHello(_ name: String = "World") {
print("Hello, \(name)!")
}
// If you want a default on the instance, too, provide one that we an override
func sayHello(_ name: String = "World") {
Self.defaultSayHello(name)
}
}
class Class<T>: Protocol {
typealias AssociatedType = T
func sayHello(name: String = "Stack Overflow") {
// Now the default behavior lives on my type
Class.defaultSayHello(name)
}
}
// But other types can get default behavior
class OtherClass<T>: Protocol {
typealias AssociatedType = T
}
Class<()>().sayHello() // Hello, Stack Overflow!
OtherClass<()>().sayHello() // Hello, World!
The one frustrating part about this is that Swift provides no way to limit defaultSayHello to implementers of Protocol. So technically anyone can call it. It can sometimes be worth prefixing it with an _ to indicate that outsiders shouldn't. This is a basic access control problem in protocols, having nothing to do with this specific question; it comes up all the time when you want "things my implementers can use on themselves, but shouldn't be called randomly." Swift doesn't have a solution for that today.
Inspired by Rob Napier's answer, here's what I went with; good old overloading for defaults:
protocol Protocol {
associatedtype AssociatedType
}
extension Protocol {
func sayHello(name: String = "World") {
print("Hello, \(name)!")
}
}
class Class<T>: Protocol {
typealias AssociatedType = T
func sayHello() {
self.sayHello("Stack Overflow")
}
}
Class<()>().sayHello() // Hello, Stack Overflow!
Class<()>().sayHello("you") // Hello, you!
This does fit my needs, but doesn't answer the question. So I'm not 100% satisfied.
I believe Rust gets this one right by allowing traits/protocols to be generic both using X<T> and associated types.