扩展到泛型结构,其中元素是泛型结构

时间:2019-05-11 20:09:44

标签: swift generics

我有一个通用结构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中执行此操作?

2 个答案:

答案 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))
        }
    }
}