[swift-evolution] Constrained Protocol Aliases

Gor Gyolchanyan gor.f.gyolchanyan at icloud.com
Mon Aug 21 04:37:32 CDT 2017

Hello, Swift community!

I'd like to start a discussion about a possibility of constrained protocol aliases. The declaration would look like this:

typealias BinaryProtocol = RandomAccessCollection & MutablCollection & RangeReplaceableCollection where Binary.Index == Int, Binary.Element == Bool

The syntax and semantics of this declaration are exactly the same as an analogous associatedtype declaration inside a protocol.
In the example above, the type BinaryProtocol represents a logical array of bits and is a generic-only protocol that is usable in any context where an integer-indexed mutable range-replaceable random-access collection is expected.
Now, it can be used in a very concise and elegant way:

public protocol BinaryInitializable {
	init<Binary>(binary: Binary) where Binary: BinaryProtocol

which would otherwise look very verbose and inelegant:

public protocol BinaryInitializable {
	init<Binary>(binary: Binary) where Binary: RandomAccessCollection & MutablCollection & RangeReplaceableCollection, Binary.Index == Int, Binary.Element == Bool

Considering that smaller sets of constraints could be aliased to their own protocol and then composited into more complex aliases, this feature would dramatically improve readability and maintainability of code that uses complex constraints, that currently leads to arcane mess:

struct Mirror {
	/// ...
	init<Subject, C where C : Collection, C.Indices : Collection, C.SubSequence : Collection, C.Indices.Index == C.Index, C.Indices.SubSequence == C.Indices, C.Iterator.Element == Mirror.Child, C.SubSequence.Index == C.Index, C.SubSequence.Indices : Collection, C.SubSequence.SubSequence == C.SubSequence, C.Indices.Iterator.Element == C.Index, C.SubSequence.Indices.Index == C.Index, C.SubSequence.Indices.SubSequence == C.SubSequence.Indices, C.SubSequence.Iterator.Element == Mirror.Child, C.SubSequence.Indices.Iterator.Element == C.Index>(_ subject: Subject, children: C, displayStyle: Mirror.DisplayStyle? = default, ancestorRepresentation: Mirror.AncestorRepresentation = default)
	/// ...

/// A collection that is its own sub-sequence
typealias RecursivelySliceableCollection = Collection where
	RecursivelySliceableCollection.SubSequence: Collection,
	RecursivelySliceableCollection.SubSequence.Element == RecursivelySliceableCollection.Element
	RecursivelySliceableCollection.SubSequence.Indices == RecursivelySliceableCollection.Indices,
		RecursivelySliceableCollection.SubSequence.SubSequence == RecursivelySliceableCollection.SubSequence

/// A collection that is its own index collection
typealias RecursivelyIndexableCollection = Collection where
	RecursivelyIndexableCollection.Indices == RecursivelySliceableCollection,
	RecursivelyIndexableCollection.Indices.Index == RecursivelyIndexableCollection.Index,

struct Mirror {
	/// ...
	init<Subject, C: RecursivelySliceableCollection & RecursivelyIndexableCollection, where  C.Element == Mirror.Child>(_ subject: Subject, children: C, displayStyle: Mirror.DisplayStyle? = default, ancestorRepresentation: Mirror.AncestorRepresentation = default)
	/// ...

Even considering that the proposal SE-0157 (https://github.com/apple/swift-evolution/blob/master/proposals/0157-recursive-protocol-constraints.md) is going to make this specific use case a non-issue, the principle applies to all cases where there are commonly used complex constraints that don't necessarily involve recursive constraints.

Specializing Generic-Only Protocols For Non-Generic Use

An additional feature that would prove to be very useful would be to make a constrained protocol alias be a non-generic-only protocol if the constraints of the alias declaration specify a same-type requirement for all its associated types, while defaulted associated types would also count.

protocol Consumer {
	associatedtype Consumable
	mutating func consume(_ consumable: Consumable) throws

var consumer0: Consumer // error: Consumer is only usable in a generic context

typealias CharacterConsumer = Consumer where  CharacterConsumer.Consumable == Character

var consumer1: CharacterConsumer // OK

The current workaround would be to declare a new protocol with protocol inheritance clauses and a where clause, but the major downside is that it introduces a completely new protocol that is not compatible with any context that expects the underlying protocols and their constraints.

Gor Gyolchanyan.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170821/c3792c22/attachment.html>

More information about the swift-evolution mailing list