purrrminator purrrminator - 7 months ago 17
Swift Question

Function that return either of two generics

I'm trying to implement

either
function, which accepts two generic container and return either of them:

func either<A,B>(a: Container<A>, b: Container<B>) -> ?either Container<A> or Container<B>? {
// choose any of container
return chosen
}


Looks like I need a protocol, that Container must conforms to, so that my
either
's return type should be of this protocol.
Is this right solution?

protocol ContainerProtocol
struct Container<T>: ContainerProtocol

func either<A: ContainerProtocol, B:ContainerProtocol, C:ContainerProtocol>(a: A, b: B) -> C {
// choose any of container
return chosen
}


UPDATE

ok so I've implemented the EitherContainer enum and the final code is following:

struct Container<T>: Unique {
typealias UnderlyingObject = T
var object: UnderlyingObject
var uniqueId: String
}

enum EitherContainer<A,B> {
case a(container: Container<A>)
case b(container: Container<B>)
}

func wrappedInput<A,B>(wra: Container<A>, wrb: Container<B>, paramClosure: (Container<A>, Container<B>) -> EitherContainer<A,B>) -> EitherContainer<A,B> {
//do some work here
return paramClosure(wra, wrb)
}

func rawInput<A, B>(a: A, b: B) -> Any {
let wrappedA = Container(object: a, uniqueId: "a")
let wrappedB = Container(object: b, uniqueId: "b")
let wrappedRes = wrappedInput(wrappedA, wrb: wrappedB) {
(a1: Container, a2: Container) -> EitherContainer<A,B> in
// do some work here
return EitherContainer.a(container: a1)
}
var rawRes: Any
switch wrappedRes {
case .a(let container):
rawRes = container.object
case .b(let container):
rawRes = container.object
}
return rawRes
}


what bothers me now, is
Any
type, which shuts the compiler up, but for me looks like a weak crutch. Again same problem
rawInput<A, B>(a: A, b: B) -> Any
.
rawInput
should return either A or B, but I'm forced to use
Any
instead. Should I add another enum for raw options? Any thoughts?

Answer

The traditional Either type looks like this:

enum Either<A, B>
{
    case Left(A)
    case Right(B)
}

and is more useful, as it's not limited to your Container type.
(Either is the "canonical" sum type.)

It would be used like this:

func wrappedInput<A, B> (
    a : Container<A>,
    b: Container<B>,
    paramClosure: (Container<A>, Container<B>) -> Either<Container<A>,Container<B>>
    ) -> Either<Container<A>, Container<B>>
{
    return Either.Left(a) // Dummy
}

func rawInput<A, B>(a: A, b: B) -> Either<A,B> {
    let wrappedA = Container(object: a, uniqueId: "a")
    let wrappedB = Container(object: b, uniqueId: "b")
    let wrappedRes = wrappedInput(wrappedA, b: wrappedB) {
        (a1: Container, a2: Container) -> Either<Container<A>, Container<B>> in
        // do some work here
        return Either.Left(a1)
    }

    switch wrappedRes {
    case .Left(let container):
        return Either.Left(container.object)
    case .Right(let container):
        return Either.Right(container.object)
    }
}