- Proposal: SE-NNNN
- Author: Matthew Johnson
- Status: Awaiting review
- Review manager: TBD
This proposal renames the *LiteralConvertible protocols to Syntax.*Literal.
| // Created by Matthew Johnson on 5/28/16. | |
| // Copyright © 2016 Anandabits LLC. All rights reserved. | |
| // | |
| // This is a minimalist implementation of a responder chain in pure Swift. | |
| // | |
| // It is not intended to demonstrate the best way to | |
| // implement event processing in Swift. | |
| // | |
| // The intent is to show how little code is necessary to acheive behavior | |
| // similar to Cocoa's responder chain in pure Swift. |
This proposal renames the *LiteralConvertible protocols to Syntax.*Literal.
The goal of this document is to provide a comprehensive view of what value subtyping might look like in Swift and demonstrate how generalized enums play a significant role in this future.
Note: All syntax used in this document that is not currently valid Swift syntax is only intended to serve the purpose of demonstrating ideas and to serve as a point of reference for future proposals. The intent is not to propose that this exact syntax be used.
Acknowledgement: some of the ideas in this document have been inspired by Niko Matsakis' blog post exploring similar ideas in the context of Rust: http://smallcultfollowing.com/babysteps/blog/2015/08/20/virtual-structs-part-3-bringing-enums-and-structs-together/
Author: Matthew Johnson
| /* | |
| If you're willing to write a little bit of boilerplate you can have type-safe GADT in Swift today. | |
| This is accomplished using a wrapper struct with a phantom type parameter that wraps a private enum value. | |
| Static factory methods on the struct wrap each case returning a value with the phantom type bound as necessary. | |
| An extension is created for each phantom type binding providng a `switch` method that requires each case | |
| with a matching type to be covered and uses `fatalError` for cases where values will never be created. | |
| New members are added in extensions that bind the phantom type and make use of the `switch` method. | |
| The example below is drawn from https://en.m.wikibooks.org/wiki/Haskell/GADT | |
| */ |
| // This example shows how higher-kinded types can be emulated in Swift today. | |
| // It acheives correct typing at the cost of some boilerplate, manual lifting and an existential representation. | |
| // The technique below was directly inspired by the paper Lightweight Higher-Kinded Polymorphism | |
| // by Jeremy Yallop and Leo White found at http://ocamllabs.io/higher/lightweight-higher-kinded-polymorphism.pdf | |
| /// `ConstructorTag` represents a type constructor. | |
| /// `Argument` represents an argument to the type constructor. | |
| struct Apply<ConstructorTag, Argument> { | |
| /// An existential containing a value of `Constructor<Argument>` | |
| /// Where `Constructor` is the type constructor represented by `ConstructorTag` |
| // This example shows how closed protocols can be emulated in Swift today. | |
| // The technique leverages Swift's support for public (but not open) classes. | |
| // First, it's worth observing that there is an almost trivial technique that can be used when | |
| // it is possible to specify a (possibly abstract) superclass for all conforiming types. | |
| // Simply declare a public (not open) base class and a protocol with a Self inheritance constraint. | |
| // Swift does not support open subclasses of a public superclass so no classes outside the module | |
| // will be able to meet the self inheritance constraint. | |
| public class FooBase {} | |
| public protocol Foo where Self: FooBase {} |
| // NOTE: This is an experiement that does not actually work (in the 2/8/18 nightly toolchain). | |
| // The idea is to use Swift's constraint inference to be able to abstract an arbitrary set of constraints. | |
| /// An empty enum serving as an example of the general case of | |
| /// representing a set of constraints that relate multiple types. | |
| /// This specific example provides the constraint that both types are sequences and they have the same Element type. | |
| enum Parallel<S1: Sequence, S2: Sequence> where S1.Element == S2.Element { | |
| typealias First = S1 | |
| typealias Second = S2 | |
| } |
| /* | |
| This gist contains an analysis of the design space for abstractions in a type system like Swift's. | |
| It focuses on Monoid as an example but the same set of tradeoffs apply to any abstraction. | |
| As such, it focuses on a single abstraction and does not explore issues that arise when designing | |
| an abstraction hierarchy in depth. | |
| The matrix below describes some of the design important design tradeoffs for various approaches including: | |
| - OO style protocols such as `Monoid { var identity: Self ... } | |
| (Haskell also adopts this approach for many abstractions, including for Monoid) | |
| - ML signature style static protocols with empty enum conformances analagous with ML structures |
Author: Matthew Johnson