Are we close to sharing implementation between throwing and non-throwing predicates?

A long time ago, I think Swift 3, I tried writing a system where a predicate-bearing algorithm can handle both throwing and non-throwing predicates without writing the meat of the algorithm twice. I couldn't get past that IteratorProtocol.next() is never throwing, and couldn't handle how to propagate the error, let alone how to conditionally propagate the error.

I just tried again, this time hoping Swift 5's Result can help. I got to:

extension Sequence {

    public func presortedSubtract<T: Sequence, U: RangeReplaceableCollection>(sortedFilterElements: T, storingAs: U.Type, by areInIncreasingOrder: (Element, Element) throws -> Bool) throws -> U where T.Element == Element, U.Element == Element {
        return try withoutActuallyEscaping(areInIncreasingOrder) { predicate in
            return try U(IteratorSequence(FullSetSubtractionImplementationIterator(makeIterator(), filter: sortedFilterElements.makeIterator(), by: predicate)).lazy.map { try $0.get() })
        }
    }

    public func presortedSubtract<T: Sequence, U: RangeReplaceableCollection>(sortedFilterElements: T, storingAs: U.Type, by areInIncreasingOrder: (Element, Element) -> Bool) -> U where T.Element == Element, U.Element == Element {
        return withoutActuallyEscaping(areInIncreasingOrder) { predicate in
            return U(IteratorSequence(FullSetSubtractionImplementationIterator(makeIterator(), filter: sortedFilterElements.makeIterator(), by: predicate)).lazy.map { try! $0.get() })
        }
    }

}

Where FullSetSubtractionImplementationIterator (based off this post of mine) vends Result<Element, Error> instead of Element. These functions originally weren't public; I tried to unify them to a single method based off rethrows:

extension Sequence {

    func presortedSubtractToo<T: Sequence, U: RangeReplaceableCollection>(sortedFilterElements: T, storingAs: U.Type, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> U where T.Element == Element, U.Element == Element {
        return try presortedSubtract(sortedFilterElements: sortedFilterElements, storingAs: U.self, by: areInIncreasingOrder)
    }

}

I was hoping the compiler will look at areInIncreasingOrder and choose the appropriate overload of presortedSubtract based on the predicate's throwing status. There was an error on the "p" of the return line with "Call can throw, but the error is not handled; a function declared 'rethrows' may only throw if its parameter does," which seems slightly contradictory. (The first part assumes it's throwing, but second if it's non-throwing.)

BTW, removing the "try" gives "Call can throw, but it is not marked with 'try' and the error is not handled; a function declared 'rethrows' may only throw if its parameter does."

Is there some way to do this now that I missed, or do I have to keep two overloads? At least I can keep the algorithm in a single iterator type, and branch on "try $0.get()" vs. "try! $0.get()" in the corresponding functions.

I think what you want would be this change in the language where every function would throw which enables you to create a single generic version of your function:

Terms of Service

Privacy Policy

Cookie Policy