Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Conditional conformances with Generic Type

Tags:

swift

I'm trying to get to work a Swift Extension with two generic types. I tried to make an example.

We have a Box where we can for different types.

class Box<E> {
    var value: E
    init(val: E) {
        value = val
    }
}

Now we have a special Itemtype which can again have different types

class Item<Type> {
    var value: Type
    init(val: Type) {
        value = val
    }
}

So now we can easily create an Box<Item<Int>>. But maybe we want to change it to Box<Item<String>> So I would like to have an extension to change from Box<Item<A>> to Box<Item<B>>

What does work is the following

extension Box where E: Item<Any> {
    func mapOnItem(function: (Any) -> Any) -> Box<Item<Any>> {
        return Box<Item<Any>>(val: Item(val: function(value.value)))
    }
}

But this is not very useful because we don't have the connection from the function return value to the mapOnItem return value.

So I tried to fix but I'm failing. My understanding would be to introduce another generic variable here.

extension Box<A> where E: Item<A> {
    func mapOnItem<B>(function: (A) -> B) -> Box<Item<B>> {
        return Box<Item<B>>(val: Item(val: function(value.value)))
    }
}

I'm getting the error

Constrained extension must be declared on the unspecialized generic type 'Box' with constraints specified by a 'where' clause

Do you have any clues for me? Is it even possible?

Thanks for your help

Martin

like image 419
Martin Avatar asked Mar 06 '19 13:03

Martin


1 Answers

A constraint on the extension can restrict the placeholder type E to concrete types or protocols, for example:

extension Box where E: Item<Any> {}
extension Box where E == String {}
extension Box where E: Numeric {}

But you cannot put a generic constraint on the extension:

extension Box<A> where E: Item<A> {}
// Error: Constrained extension must be declared on the unspecialized generic
// type 'Box' with constraints specified by a 'where' clause

The solution is to restrict the method instead:

extension Box  {
    func mapOnItem<A, B>(function: (A) -> B) -> Box<Item<B>> where E: Item<A> {
        return Box<Item<B>>(val: Item(val: function(self.value.value)))
    }
}
like image 129
Martin R Avatar answered Nov 04 '22 21:11

Martin R