3
votes
internal protocol Reducer {
        associatedtype S : BaseState
        associatedtype A : BaseAction
        
        func reduce(state: S, action: A) -> S
    }
    
    internal class ReducerImpl : Reducer {
        func reduce(state: MainState, action: MainAction) -> MainState { //<-- Error because MainAction is a protocol not a concrete one.
            return state
        }
    }
    
    internal class MainState : BaseState {}
    internal protocol MainAction : BaseAction {}
    
    internal protocol BaseState {}
    internal protocol BaseAction {}

If I change MainAction from protocol to class, the compile error disappears.

I've searching many articles to understand of this error but failed.

Do I have to pass a concrete parameter(eg. enum, class, struct) in reduce(...) function?

I want to make ReducerImpl can take various action type that conform MainAction.

Is there anyone who can give me explain about that error and why the Swift adopt this kind of rule.

1
This feels like a very elaborate way to express a simple function. Can you give an example of the calling code that relies on this protocol? The fact that you need "Base..." protocols suggests your trying to reinvent class-inheritance with protocols (also the fact that you call it a "subtype" which is not a good way to think about protocols that require other protocols). Start with the calling code and the algorithms that you're trying to implement, and the protocols will follow. A protocol is not an abstract class. It is a set of requirements with semantics, allowing you to write algorithms. - Rob Napier
The more I look at this, the only possible (pure, non-crashing) implementation of this protocol is ReducerImpl. It is literally impossible to make use of the action parameter. I know you've simplified things for the question, but there comes a point when you've removed the point of the question. It is impossible to use this protocol for a useful purpose, which suggests you've removed the part that will provide the answer. - Rob Napier
What do you need BaseState and BaseState? I assume the reducers can operate on a variety of data types, thus constraining all of them to a common protocol for consuming it in the reducer doesn't seem feasible. Drop the two Base protocols, they cause more problem than they solve. - Cristik

1 Answers

2
votes

associatedtype has to be concrete, ie. a type and not a protocol.

What you can do is to create a narrower protocol with a generic function that accepts a MainAction parameter:

internal protocol MainReducer {
    associatedtype State
    func reduce<Action>(state: State, action: Action) -> State where Action: MainAction
}

internal class ReducerImpl: MainReducer {
    func reduce<Action>(state: MainState, action: Action) -> MainState where Action: MainAction {
        return state
    }
}