我有一个通用结构FutureValue<Element>
和Failable<Element>
,它们都实现了map
…
struct FutureValue<Element> {
func map<U>(_ t: (Element) -> U) -> FutureValue<U> …
}
struct Failable<Element> {
func map<U>(_ t: (Element) -> U) -> Failable<U> …
}
我想在FutureValue
上写一个扩展名,以在Element
为任何 Failable
时对其进行专门化,以便实现{ 1}}类似的函数,它映射到map
Element
如何在Swift中执行此操作?
答案 0 :(得分:3)
您只需要创建一个协议即可捕获“任何失败”并捕获算法所需的片段。
protocol AnyFailable {
associatedtype Element
func map<U>(_ t: (Element) -> U) -> Failable<U>
}
并表示所有故障均是AnyFailable。
extension Failable: AnyFailable {}
您可能想在协议上添加方法以提取所需的数据或提供方法。
然后,创建您的扩展程序:
extension FutureValue where Element: AnyFailable {
func map<U>(_ t: (Element.Element) -> U) -> FutureValue<Failable<U>> {
// You will probably need to provide your own implementation here
return FutureValue<Failable<U>>(element: element.map(t))
}
}
值得注意的是我是如何构造的。我首先基于String
编写了一个更具体的表格(只是选择一个随机的东西):
extension FutureValue where Element == Failable<String> {
func map<U>(_ t: (String) -> U) -> FutureValue<Failable<U>> {
...
}
}
我写了一个简单的消费代码:
let f = FutureValue(element: Failable(element: "alice"))
print(f.map { $0.first })
从那里,我将所需的片段提取到协议中。这会使您逐步朝着正确的方向前进。有时直接跳转到最通用的形式非常困难。
答案 1 :(得分:1)
非常感谢Rob的super answer。
最后我采用的方法略有不同,因此我将其添加为第二个答案。对于某个泛型的扩展(受某种元素限制)的情况,我觉得这种方法更简单。它也是一种易于引入的“模式”,可以在类似情况下轻松使用。
/*
Protocol for things that can be _concretely_ represented as a `Failable`.
I keep it private so it's just used to constrain the protocol extension
below.
*/
private protocol AsFailable {
associatedtype Element
var asFailable: Failable<Element> {get}
}
/*
`Failable` can definitely be represented `AsFailable`…
*/
extension Failable: AsFailable {
var asFailable: Failable<Element> {
return self
}
}
/*
Use the `AsFailable` protocol to constrain an extension to `FutureValue`
for any `FutureValue` who's `Element` is a `Failable`.
*/
extension FutureValue where Element: AsFailable {
func happyMap<U>(_ t: @escaping (Element.Element) -> U)
-> FutureValue<Failable<U>> {
return map { $0.asFailable.map(t) }
}
}
Rob的方法允许我实现map
(如OP中所述),但是当我也想实现flatMap
时,我就开始陷入困境。切换为使用AsFailable
可以让我快速编写flatMap
的简单实现。
我认为AsXXX
方法对于这样的情况比较简单,在这种情况下,仅需要 协议来充当约束。
happyFlatMap
如下所示:
func happyFlatMap<U>(_ t: @escaping (FailableElement) -> FutureValue<Failable<U>>)
-> FutureValue<Failable<U>>
{
typealias Out = FutureValue<Failable<U>>
return flatMap {
failable in
switch failable.asFailable {
case let .happy(element):
return t(element)
case let .error(error):
return Out(Failable<U>.error(error))
case let .canceled(reason):
return Out(Failable<U>.canceled(reason))
}
}
}