Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Generalized Existentials

Redundant introduction aside, I want to have something like this:

let collection : Any<Sequence where .Iterator.Element == String> = ...

or

let collection : Sequence<where .Iterator.Element == String> = ...

This is referred to as "Generalized existentials" in Apple's Generics Manifesto. (I think) I really need this for a number of use cases and this:

protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements.

makes "the first Protocol Oriented Language" be cumbersome for me to understand. Lack of this makes me fight Swift's type system and create adverse generic "abstract" classes where there should be a protocol with associatedtype.

Here's one example, that hit me the most, delegate for a generic class:

protocol GenericClassDelegate : class {
    associatedtype ItemType
    func didDoThat(who : GenericClass<ItemType>) 
}

class GenericClass<T> {
    weak var delegate : GenericClassDelegate<where .ItemType == T>? // can't do that

    func notify() {
        delegate?.didDoThat(who: self)
    }
}

While I can describe GenericClassDelegate protocol, I (currently in Swift 3) can't have a variable or constant of that type (or any type conforming to restrictions).

Don't confuse this question with How to use generic protocol as a variable type or Swift delegate protocol for generic class, as my questions are:

  1. Are there any proposals or discussions currently present on introducing Generalized Existentials into Swift, what are the plans? If no, how can I participate and affect this?
  2. If Swift was designed that way (with Associated Types, but without Generalized Existentials), maybe it implies some architectural shift. What am I expected to replace delegation pattern with?

P.S. Don't suggest thunks with type erasure when you capture delegate's function in a closure, that is so much wrong and misleading, I'd even call it a crutch.

Accidentally found another solution, but I'm not satisfied with it completely:

protocol GenericClassDelegate : class {
    associatedtype ItemType
    func didDoThat(who : GenericClass<ItemType, Self>)
}

class GenericClass<T, Delegate : GenericClassDelegate> where Delegate.ItemType == T {
    weak var delegate : Delegate?

    func notify() {
        delegate?.didDoThat(who: self)
    }

    init(_ delegate : Delegate?) {
        self.delegate = delegate
    }
}

// Delegates must be final classes, otherwise it does not compile
// because I used Self in GenericClassDelegate
final class GenericClassDelegateImp<T> : GenericClassDelegate {
    typealias ItemType = T
    func didDoThat(who: GenericClass<T, GenericClassDelegateImp>) {
        print(who)
    }
}

// Usage:
var delegate = GenericClassDelegateImp<Int>()
var genericClass = GenericClass<Int, GenericClassDelegateImp<Int>>(delegate)
like image 858
MANIAK_dobrii Avatar asked Sep 13 '16 11:09

MANIAK_dobrii


People also ask

What is existential types in Swift?

Existentials in Swift allow defining a dynamic value conforming to a specific protocol. Using primary associated types, we can constrain existentials to certain boundaries. The Swift team introduced the any keyword to let developers explicitly opt-in to a performance impact that might otherwise not be visible.

What is Associatedtype in Swift?

An associated type gives a placeholder name to a type that's used as part of the protocol. The actual type to use for that associated type isn't specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.

What are generics Swift?

Swift Generics allows us to create a single function and class (or any other types) that can be used with different data types. This helps us to reuse our code.

What is an existential type?

Existential types, or 'existentials' for short, are a way of 'squashing' a group of types into one, single type. Existentials are part of GHC's type system extensions.


2 Answers

Are there any proposals or discussions currently present on introducing Generalized Existentials into Swift, what are the plans? If no, how can I participate and affect this?

It is a commonly requested feature, and there was already preliminary design effort on swift-evolution. But at this moment, the core team and the community are focusing on ABI stability affecting features, or what Lattner defines as "Swift 4 Phase 1".

You would definitely hear more about it when Phase 2 commences. Given its popularity, it is expected to be part of Swift 4.

If Swift was designed that way (with Associated Types, but without Generalized Existentials), maybe it implies some architectural shift. What am I expected to replace delegation pattern with?

You can use type-erased wrappers as a transitive solution. In general, it exploits the dynamic dispatch and the inheritance of classes to erase the type.

protocol Fancy {
    associatedtype Value
    var value: Value
}

struct FancyMatter<Value> {
    let value: Value
}

class AnyFancyBoxBase<P: FancyProtocol>: AnyFancyBox<P.Value> {
    let base: P
    override var value: P.Value { return base.value }
    init(_ base: P) { self.base = base }
}

class AnyFancyBox<Value> {
    var value: Value { fatalError() }
}

var box: AnyFancyBox<Int> = AnyFancyBoxBase(FancyMatter(1))

You may take a look at how the Standard Library implements type-erased wrappers.

like image 160
Anders Avatar answered Oct 23 '22 14:10

Anders


That code is not correct. The correct code, freshly compiled, is:


protocol Fancy {
    associatedtype Value
    var value: Value {get set}
}


struct FancyMatter<Value>: Fancy  {
    var value: Value
}


class AnyFancyBoxBase<P: Fancy>: AnyFancyBox<P.Value> {
    let base: P
    override var value: P.Value {return base.value }
    init(_ base: P) { self.base = base }
}


class AnyFancyBox<Value> {
    var value: Value {fatalError() }
}


var box: AnyFancyBox<Int> = AnyFancyBoxBase(FancyMatter(value: 1))

// prove it out
print (AnyFancyBoxBase(FancyMatter(value: 1)).value)
print (box.value)


like image 23
johnrubythecat Avatar answered Oct 23 '22 13:10

johnrubythecat