基于消息的体系结构中的通用消息
Generic messages in message-based architecture
我正在 Swift 中试验基于消息的架构。例如,我正在尝试做一些类似于 Elm 架构的事情。这是我的代码的样子:
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
来更改它。如果有任何副作用需要计算,它们将被 return 编辑为 SideEffect
消息,并将由其他人处理。每个 SideEffect
消息都有一个“回调”参数,一个 Message
在副作用完成时发送。这很好用。
现在,如果我想要一个通用的副作用消息怎么办?我想要这样的东西:
struct Request<ReturnType> { … }
加载请求和 return 类型 ReturnType
:
的值有相关的副作用
enum SideEffect<Message> {
case sendRequest(Request<T>, (T) -> Message)
}
但这(显然)无法编译,因为 case
必须是通用的 T
。我无法使整个 SideEffect
通用于 T
,因为还有其他与 T
.
无关的副作用
我能否以某种方式创建带有 Request<T>
的 SideEffect
消息,稍后会发送带有 T
的 Message
消息? (我想我想要 this feature discussed on swift-evolution 之类的东西。)
您需要键入 erase T
– 通常这可以通过闭包来完成,因为它们可以从创建它们的站点引用上下文,而无需将该上下文暴露给外界。
例如,使用模拟 Request<T>
(假设它是一个异步操作):
struct Request<T> {
var mock: T
func doRequest(_ completion: @escaping (T) -> Void) {
// ...
completion(mock)
}
}
我们可以构建一个 RequestSideEffect<Message>
来保存一个接受给定 (Message) -> Void
回调的闭包,然后对捕获的 Request<T>
实例执行请求,通过 Request<T>
转发结果=19=],然后可以将其结果传递回回调(因此在闭包中保留类型变量 T
'contained'):
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([=11=]))
}
}
}
func doRequest(_ completion: @escaping (Message) -> Void) {
_doRequest(completion)
}
}
现在您的 SideEffect<Message>
可以看起来像这样:
enum SideEffect<Message> {
case sendRequest(RequestSideEffect<Message>)
}
你可以这样实现 State
:
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:
let sideEffect = RequestSideEffect(
request: Request(mock: "foo"), nextMessage: Message.receiveResponse
)
return [.sendRequest(sideEffect)]
case .receiveResponse(let response):
something = response
return []
}
}
}
var s = State(something: "hello")
let sideEffects = s.send(msg: .downloadSomething)
for case .sendRequest(let sideEffect) in sideEffects {
sideEffect.doRequest {
_ = s.send(msg: [=13=]) // no side effects expected
print(s) // State(something: "foo")
}
}
我正在 Swift 中试验基于消息的架构。例如,我正在尝试做一些类似于 Elm 架构的事情。这是我的代码的样子:
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
来更改它。如果有任何副作用需要计算,它们将被 return 编辑为 SideEffect
消息,并将由其他人处理。每个 SideEffect
消息都有一个“回调”参数,一个 Message
在副作用完成时发送。这很好用。
现在,如果我想要一个通用的副作用消息怎么办?我想要这样的东西:
struct Request<ReturnType> { … }
加载请求和 return 类型 ReturnType
:
enum SideEffect<Message> {
case sendRequest(Request<T>, (T) -> Message)
}
但这(显然)无法编译,因为 case
必须是通用的 T
。我无法使整个 SideEffect
通用于 T
,因为还有其他与 T
.
我能否以某种方式创建带有 Request<T>
的 SideEffect
消息,稍后会发送带有 T
的 Message
消息? (我想我想要 this feature discussed on swift-evolution 之类的东西。)
您需要键入 erase T
– 通常这可以通过闭包来完成,因为它们可以从创建它们的站点引用上下文,而无需将该上下文暴露给外界。
例如,使用模拟 Request<T>
(假设它是一个异步操作):
struct Request<T> {
var mock: T
func doRequest(_ completion: @escaping (T) -> Void) {
// ...
completion(mock)
}
}
我们可以构建一个 RequestSideEffect<Message>
来保存一个接受给定 (Message) -> Void
回调的闭包,然后对捕获的 Request<T>
实例执行请求,通过 Request<T>
转发结果=19=],然后可以将其结果传递回回调(因此在闭包中保留类型变量 T
'contained'):
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([=11=]))
}
}
}
func doRequest(_ completion: @escaping (Message) -> Void) {
_doRequest(completion)
}
}
现在您的 SideEffect<Message>
可以看起来像这样:
enum SideEffect<Message> {
case sendRequest(RequestSideEffect<Message>)
}
你可以这样实现 State
:
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:
let sideEffect = RequestSideEffect(
request: Request(mock: "foo"), nextMessage: Message.receiveResponse
)
return [.sendRequest(sideEffect)]
case .receiveResponse(let response):
something = response
return []
}
}
}
var s = State(something: "hello")
let sideEffects = s.send(msg: .downloadSomething)
for case .sendRequest(let sideEffect) in sideEffects {
sideEffect.doRequest {
_ = s.send(msg: [=13=]) // no side effects expected
print(s) // State(something: "foo")
}
}