These are fantastic proposals! With these in place, there would be precious little to envy Haskell.
For example, regarding “counter-proposal against making generic parameters public”, the current behaviour of Swift is essentially a bug, as reported by Noah Blake (https://bugs.swift.org/browse/SR-1065 <https://bugs.swift.org/browse/SR-1065>). Then there is: > (3) Using variadic patterns (shamefully borrowing the `...` notation from C++ > here to mean an arbitrary-length list of patterns to match or expressions to > expand): > > extension (T...) : Equatable where T : Equatable... {} > > func == <T...>(lhs: (T...), rhs: (T...)) -> Bool where T : Equatable... { > /* ... */ } Not only that this would make certain Standard Library signatures less embarrassing, but would enable entirely new programming patterns. For example, the size of a fixed-length sequence could be type guaranteed like so: let v = Vector<_2,_0,_1,_6>(repeatedValue: 42) v.count // 2016 … wehere types like _2 and _6 may be caseless enums: enum _2 : DecimalDigitType { } milos > On 7 Apr 2016, at 09:44, Pyry Jahkola via swift-evolution > <[email protected]> wrote: > > Jordan, > > Your comments brought up a few more closely related ideas that have been > bubbling under. > > To everyone, > > Sorry for going beyond topic here. The discussion of the further proposals > below should be taken into their own threads if there's interest. I'm just > trying to motivate what else moving out the `where` clause would make > possible. > > 1. My counter-proposal against making generic parameters public > >> Another factor here is that we've been planning for a while for generic >> parameters to types to be exposed as implicit member typealiases, since they >> already conflict with the namespace for member typealiases. Therefore it's >> important to name generic parameters to types well, but less important to do >> so for generic parameters for functions. (They're also much less likely to >> be ad hoc for types; there has to be something that describes the relation >> between the Self type and the parameter, while the function might not have >> anything more interesting than "operand".) > > I disagree with that. I think it's more natural to restrict generic type > parameters to the immediate local scope of the class/struct/enum definition > or extension, and simply allow making the type public with `typealias`. For > example, this would make `Element` a public member of `Array`: > > public enum Result<T, Error> { > public typealias Value = T // This makes `Value` public for > extensions and everyone > } > > I would even allow publishing the otherwise local name by repeating it in > `typealias` like this: > > public struct Array<Element> { // This `Element` is only available in > (the missing) where clause and the scope below. > public typealias Element = Element // This line makes `Element` > available everywhere, see below. > } > > extension Array<T> { // <- Regardless of the pattern (`T`) used here, > var mid: Element? { // the type `Element = T` is available here > return ... // because it was made public elsewhere. > } > } > extension Array<Optional<T>> { // <- An example of pattern matching (see > further below). > // Ditto, `Element = T?` > } > > typealias Ints = [Int] > let x: Ints.Element = ... // Int > > Next, I propose how to extend this syntax with pattern matching. The above > thinking is a natural progression from the use of pattern matching for > introducing generic type parameters in type `extension`s. > > > 2. Proposal to enable pattern matching of generic types in generic parameters > >> This is also a minor point against declaring generic parameters for >> extensions, since they would have to match up. We also do need a syntax some >> day to represent new parameters: >> >> // For expository purposes only: >> extension <T> Array where Element == Optional<T> >> >> But that deserves its own proposal. I just don't want us to paint ourselves >> into a corner. > > I agree that we need that feature. But instead of your proposed syntax, I'd > take influence from the already existing pattern matching that we have at > value level, such that: > > 1. any generic type expressions within the angle brackets `<...>` are taken > as patterns to match against (e.g. `Array<Element>`, `Optional<Foo>`, > `Dictionary<S, S>`), and > 2. all non-generic type identifiers in those expressions are taken as generic > parameters and not concrete types (`Element`, `Foo`, or `S`, respectively). > > Your example would translate into the first of these two: > > extension Array<Optional<T>> { // extending [T?] doesn't even need any > `where` clause > func unwrapAll() -> [T] { ... } > } > > extension Dictionary<K, Dictionary<K, V>> { // This pattern match > requires the same `K` twice. > subscript(x: K, y: K) -> V { ... } > } > > The generic parameters would shadow any existing names in the outer scope, > i.e. `extension Array<String>` would mean the same as `extension > Array<Element>`. But it would be a good idea to add a warning (or linter > error) at least when stdlib types such as `Swift.String` are shadowed like > that. I think the benefits from pattern matching outweigh the possible > confusion, especially since you can't use any members of `String` if you > mistakenly wrote `extension Array<String> { ... }` instead of `extension > Array<T> where T == String`. > > With this syntax, we could also allow extending Array, Dictionary, and > Optional in their more natural notation: > > extension [T?] { > func unwrapAll() -> [T] { /* ... */ } > } > > extension [K: [K: V]] { > subscript(x: K, y: K) -> V { /* ... */ } > } > > Here are a few more (somewhat realistically needed) examples with further > `where` clauses: > > extension [T] where T : Hashable { /* ... */ } > extension T? where T : Comparable { /* ... */ } > extension [K: V] where V : Equatable { /* ... */ } > extension [T] where T == String { /* ... */ } > > I think pattern matching is a very expressive, intuitive, and readable > technique that works quite well for this purpose, better than what we > currently have. > > > 3. Future directions > > Brent already pointed out that the `where` constraint syntax could be used > for dependent types (i.e. type-level values as generic parameters). Four more > possible directions come to my mind: > > (1) Adding conditional protocol conformances: > > extension [T]: Equatable where T : Equatable { /* ... */ } > extension [T]: Comparable where T : Comparable { /* ... */ } > extension [K, V]: Equatable where V : Equatable { /* ... */ } > extension Foo<X, Bar<Y>> : Bazzable where X : Baz, Y : Baz { /* ... */ } > > (2) Extending non-nominal types: > > extension (A, B) { var tail: B { /* ... */ }} > extension (A, B, C) { var tail: (B, C) { /* ... */ }} > > (3) Using variadic patterns (shamefully borrowing the `...` notation from C++ > here to mean an arbitrary-length list of patterns to match or expressions to > expand): > > extension (T...) : Equatable where T : Equatable... {} > > func == <T...>(lhs: (T...), rhs: (T...)) -> Bool where T : Equatable... { > /* ... */ } > > (4) Since all type constraints with `:` fly out to of the generic parameter > list into the `where` clause, we could enable the use of colons to give > labels to generic parameters (and thus even make some of them have default > values): > > struct Parser<encoding: Encoding, input: Input> > where Encoding : EncodingProtocol /*, ... */ > { > // ... > } > > let parser = Parser<encoding: UTF8, input: String>() > > — Pyry > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
