Search code examples
swifttypesprotocols

Use of protocol as a type must be written with any (Learning Purposes)


I've translated code from a functional programming language to Swift, and it works well when verifying via variables. However, I encounter an error when testing, attributed to Streamable and any Streamable incompatibility. Here's the context:

The Streamable protocol acts as a type constraint for generics or for declaring protocol-conforming variables. Conversely, any Streamable signifies a type conforming to Streamable, used in closures to capture or return such types.

How can I utilize Streamable for funnyNumberStream?

Btw I made it so simple to follow, so should not be hard to understand.

protocol Streamable {
    associatedtype Element
    func next() -> (Element, Self)
}

struct OnesStream: Streamable {
    func next() -> (Int, OnesStream) {
        return (1, OnesStream())
    }
}

struct LazyNaturalStream: Streamable {
    private var current: Int
    
    init(start: Int = 1) {
        self.current = start
    }
    
    func next() -> (Int, LazyNaturalStream) {
        let result = current
        return (result, LazyNaturalStream(start: current + 1))
    }
}

func streamForNSteps<S: Streamable>(_ s: S, _ n: Int) -> [S.Element] {
    if n == 0 {
        return []
    } else {
        let next = s.next()
        return [next.0] + streamForNSteps(next.1, n - 1)
    }
}

let funnyNumberStream: any Streamable = {
    struct FunnyStream: Streamable {
        var localLazyNaturalStream = LazyNaturalStream()
        
        func next() -> (Int, FunnyStream) {
            let currentValue = localLazyNaturalStream.next()
            
            if currentValue.0 % 5 == 0 {
                return (-currentValue.0, FunnyStream(localLazyNaturalStream: currentValue.1))
            } else {
                return (currentValue.0, FunnyStream(localLazyNaturalStream: currentValue.1))
            }
        }
    }
    
    return FunnyStream()
}()

// Example usage
// [1, 2, 3, 4, -5, 6, 7, 8, 9, -10, 11, 12, 13, 14, -15, 16]
let a = streamForNSteps(funnyNumberStream, 16)

The example from above it works perfectly fine, the test cases are the problem.

Test Cases:

final class hw4Test: XCTestCase {
    
    func testFunnyNumberStream() throws {
        // Act
        let caseOneResult = streamForNSteps(funnyNumberStream, 0)
        let caseTwoResult = streamForNSteps(funnyNumberStream, 16)
        let caseThreeResult = streamForNSteps(funnyNumberStream, 5)
        
        // Assert
        XCTAssertEqual(caseOneResult, []) // Type 'Any' cannot conform to 'Equatable'
        XCTAssertEqual(caseTwoResult, [1, 2, 3, 4, -5, 6, 7, 8, 9, -10, 11, 12, 13, 14, -15, 16]) // Type 'Any' cannot conform to 'Equatable'
        XCTAssertEqual(caseThreeResult, [1, 2, 3, 4, -5]) // Type 'Any' cannot conform to 'Equatable'
    }
}

Any explanation is welcomed!!!


Solution

  • let funnyNumberStream: any Streamable = { ... }
    

    This indicates that funnyNumberStream not only can be implemented by any Streamable type, but any Streamable type for any Element type. What you seem to mean is that it is supposed to be a stream of Int. That is most easily expressed with a primary associated type:

    protocol Streamable<Element> {  // <--- Add <Element> as the primary associated type
        associatedtype Element
        func next() -> (Element, Self)
    }
    

    And then you can constrain your variable to a Streamable that returns Int:

    let funnyNumberStream: any Streamable<Int> = { ... }