基于消息的体系结构中的通用消息

时间:2017-09-16 06:41:08

标签: swift generics elm

我正在Swift中尝试基于消息的架构。我正在尝试做类似于Elm Architecture的事情。这就是我的代码的样子:

enum SideEffect<Message> {

    case sendRequest((String) -> Message)
}

protocol Component {

    associatedtype Message

    mutating func send(msg: Message) -> [SideEffect<Message>]
}

struct State: Component {

    var something: String?

    enum Message {

        case downloadSomething
        case receiveResponse(String)
    }

    mutating func send(msg: Message) -> [SideEffect<Message>] {
        switch msg {
            case .downloadSomething:
                return [.sendRequest(Message.receiveResponse)]
            case .receiveResponse(let response):
                something = response
                return []
        }
    }
}

因此状态由State建模,您可以通过发送Message来更改状态。如果要计算任何副作用,它们将作为SideEffect消息返回,并由其他人处理。每个SideEffect消息都带有一个“回调”参数,一个Message在副作用结束时发送。这很有效。

现在,如果我想要一个通用的副作用消息怎么办?我想有这样的事情:

struct Request<ReturnType> { … }

并且有相关的副作用来加载请求并返回类型为ReturnType的值:

enum SideEffect<Message> {
    case sendRequest(Request<T>, (T) -> Message)
}

但是这显然不会编译,因为case必须是T的通用。我无法将整个SideEffect泛化为T,因为其他副作用与T无关。

我可以以某种方式创建一个SideEffect消息,其中Request<T>稍后会发送Message T吗? (我想我想要像this feature discussed on swift-evolution这样的东西。)

1 个答案:

答案 0 :(得分:1)

您需要键入erase T - 通常可以使用闭包来完成,因为它们可以从创建它们的站点引用上下文,而不会将该上下文暴露给外部世界。

例如,使用模拟Request<T>(假设它是异步操作):

struct Request<T> {

    var mock: T

    func doRequest(_ completion: @escaping (T) -> Void) {
        // ...
        completion(mock)
    }
}

我们可以构建一个RequestSideEffect<Message>,它包含一个接收给定(Message) -> Void回调的闭包,然后在捕获的Request<T>实例上执行请求,通过{{1转发结果然后,其结果可以传递回回调(从而保持闭包中包含的类型变量(T) -> Message'):

T

现在您的struct RequestSideEffect<Message> { private let _doRequest: (@escaping (Message) -> Void) -> Void init<T>(request: Request<T>, nextMessage: @escaping (T) -> Message) { self._doRequest = { callback in request.doRequest { callback(nextMessage($0)) } } } func doRequest(_ completion: @escaping (Message) -> Void) { _doRequest(completion) } } 可能如下所示:

SideEffect<Message>

您可以像这样实施enum SideEffect<Message> { case sendRequest(RequestSideEffect<Message>) }

State