swift 元素为泛型结构的泛型结构的扩展

u5rb5r59  于 2023-04-04  发布在  Swift
关注(0)|答案(3)|浏览(75)

我有一个泛型结构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上写一个扩展,当它的ElementanyFailable时专门化它,这样我就可以实现一个类似map的函数,该函数Map到FutureValue<Failable<Element>>中包含的Element上。
如何在Swift中做到这一点?

ruyhziif

ruyhziif1#

您只需要创建一个协议,捕获“任何Failable”并捕获您想要的算法片段。

protocol AnyFailable {
    associatedtype Element
    func map<U>(_ t: (Element) -> U) -> Failable<U>
}

并表示所有Failables都是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 })

从那里,我提取了我需要的部分到协议中。这往往会让你朝着正确的方向前进,一步一步。有时直接跳到最通用的形式是非常具有挑战性的。

xpcnnkqh

xpcnnkqh2#

非常感谢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))
        }
    }
}
kcwpcxri

kcwpcxri3#

有一种方法可以在没有附加协议的情况下为扩展中的函数指定这样的条件:

extension FutureValue {
    func map<In, Out>(_ t: (In) -> Out) -> FutureValue<Failable<Out>> where FutureElement == Failable<In> { .init() }
}

相关问题