Synthesizing Equatable, Hashable, and Comparable for tuple types

Does that mean that once structural types can conform to protocols, would the core team want to remove Optional as a nominal type and just use “T?”?

Yes; at least, it’s a direction we’ve discussed a number of times.

Or has that ship sailed because of source compatibility and you just don’t want to introduce any new nominals that shadow structurals?

typealias Optional<T> = T?

Should address source compatibility.

  - Doug

···

Sent from my iPhone

On Nov 21, 2017, at 3:46 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Tue, Nov 21, 2017 at 3:10 PM Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 3:05 PM, Slava Pestov <spestov@apple.com> wrote:

On Nov 21, 2017, at 6:02 PM, David Hart <david@hartbit.com> wrote:

Can somebody explain to me what are
nominal and structural types and why Optional should really be a structural type?

Nominal types are structs, enums, protocols and classes. They have declarations in source and are uniquely identified by their name. Structural types are functions, meta types, protocol compositions, and tuples.

The distinction is not as clear as you might think because as Chris said, you can imagine tuple being implemented as ‘struct Tuple<T…>’, similarly functions and meta types could also be written as such. It is mostly a syntactic distinction in the language, but it has far-reaching consequences in internal representations, which is why for now it would be tricky to have structural types conform to protocols.

Right. Optionals are a tricky case because we wanted them to be nominal so we could write extensions on them and make them conform to protocols, but, they’re so integral to the language that they are really treated like a structural type.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

-Chris

···

On Nov 21, 2017, at 7:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Nov 21, 2017, at 3:46 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

Does that mean that once structural types can conform to protocols, would the core team want to remove Optional as a nominal type and just use “T?”?

Yes; at least, it’s a direction we’ve discussed a number of times.

Or has that ship sailed because of source compatibility and you just don’t want to introduce any new nominals that shadow structurals?

typealias Optional<T> = T?

Should address source compatibility.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

  - Doug

···

On Nov 21, 2017, at 9:21 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 7:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Nov 21, 2017, at 3:46 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

Does that mean that once structural types can conform to protocols, would the core team want to remove Optional as a nominal type and just use “T?”?

Yes; at least, it’s a direction we’ve discussed a number of times.

Or has that ship sailed because of source compatibility and you just don’t want to introduce any new nominals that shadow structurals?

typealias Optional<T> = T?

Should address source compatibility.

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

I’m totally aware of that and mentioned it upthread. There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

-Chris

···

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

  - Doug

···

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:
On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

Does that mean that once structural types can conform to protocols, would
the core team want to remove Optional as a nominal type and just use “T?”?
Or has that ship sailed because of source compatibility and you just don’t
want to introduce any new nominals that shadow structurals?

···

On Tue, Nov 21, 2017 at 3:10 PM Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 3:05 PM, Slava Pestov <spestov@apple.com> wrote:

On Nov 21, 2017, at 6:02 PM, David Hart <david@hartbit.com> wrote:

Can somebody explain to me what are
nominal and structural types and why Optional should really be a
structural type?

Nominal types are structs, enums, protocols and classes. They have
declarations in source and are uniquely identified by their name.
Structural types are functions, meta types, protocol compositions, and
tuples.

The distinction is not as clear as you might think because as Chris said,
you can imagine tuple being implemented as ‘struct Tuple<T…>’, similarly
functions and meta types could also be written as such. It is mostly a
syntactic distinction in the language, but it has far-reaching consequences
in internal representations, which is why for now it would be tricky to
have structural types conform to protocols.

Right. Optionals are a tricky case because we wanted them to be nominal so
we could write extensions on them and make them conform to protocols, but,
they’re so integral to the language that they are really treated like a
structural type.

- Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Just throwing an idea out: what about

      Function<Double, InoutParam<String>, Param<Int>>

or

      Function<Double, Param<String, .inout>, Param<String>>

The latter assuming we had generics with value parameters (which is another common topic on this list) and defaults.
The first example might have the advantage of making use of inheritance between InoutParam and Param (which would require variance for type parameters, another common topic).

-Thorsten

···

Am 22.11.2017 um 06:25 schrieb Douglas Gregor via swift-evolution <swift-evolution@swift.org>:

On Nov 21, 2017, at 9:21 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 7:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Nov 21, 2017, at 3:46 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

Does that mean that once structural types can conform to protocols, would the core team want to remove Optional as a nominal type and just use “T?”?

Yes; at least, it’s a direction we’ve discussed a number of times.

Or has that ship sailed because of source compatibility and you just don’t want to introduce any new nominals that shadow structurals?

typealias Optional<T> = T?

Should address source compatibility.

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

···

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:
On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

See Thorsten’s response with, e.g.,

        Function<Double, InoutParam<String>, Param<Int>>

which handles “inout” by adding wrappers around the parameter types (which one would have to cope with in any user of Function), but still doesn’t handle argument labels. To handle argument labels, we would need something like strings as generic arguments. We’d also need to handle calling conventions and anything else we invent for function types.

Oh ok, I get. The ugliness comes from trying to shoehorn structural types into nominal types.

···

On 22 Nov 2017, at 07:54, Douglas Gregor <dgregor@apple.com> wrote:

On Nov 21, 2017, at 10:48 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:
On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:
On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

  - Doug

Oh, good! A use case for “literals as generic parameters” other than Vectors and Fixed-Size Arrays!

- Dave Sweeris

···

On Nov 21, 2017, at 22:54, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:48 PM, David Hart <david@hartbit.com> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

See Thorsten’s response with, e.g.,

        Function<Double, InoutParam<String>, Param<Int>>

which handles “inout” by adding wrappers around the parameter types (which one would have to cope with in any user of Function), but still doesn’t handle argument labels. To handle argument labels, we would need something like strings as generic arguments.

I think this thread derailed quite a bit — it might have even been a good thing that some discussion accidentally went off-list ;-)

Personally, I think it's nice if tuples and structs would converge more, so that either struct is seen as an extension of the tuple-concept, or tuple is considered to be an (restricted) anonymous struct.
E.g.

struct StructPoint {
  let x: Float
  let y: Float
}

typealias TuplePoint = (x: Float, y: Float)

would more or less have the same abilities (allowing extensions and protocol-conformance).
Constructors for tuples would afaics be easy, and maybe it would even be possible to initialize some structs from tuple-literals.

story until the very last step, when you send the buffer to the drawing
library (cairo, opengl, etc). all the stages that come before are swift and
involve passing data between two swift interfaces.

···

On Tue, Nov 28, 2017 at 3:18 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Nov 28, 2017 at 00:32 Kelvin Ma <kelvin13ma@gmail.com> wrote:

On Mon, Nov 27, 2017 at 9:43 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Nov 27, 2017 at 5:56 PM, Kelvin Ma <kelvin13ma@gmail.com> wrote:

On Mon, Nov 27, 2017 at 4:21 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Nov 27, 2017 at 15:46 Taylor Swift <kelvin13ma@gmail.com> >>>>> wrote:

they use packed buffers of floats, which for type safety are better
rebound to a structured type. right now (Float, Float, Float) works because
the tuple is laid out contiguously and the GPU can’t tell the difference
but it’s not guaranteed. also you’re ignoring all the CPU stages that occur
before anything even gets sent to the GPU.

Precisely, there is no guarantee of performance if you call these APIs
with an array of Swift tuples.

which is exactly why i support a dedicated language-level vector type
which guarantees contiguous, fixed storage, and which the compiler knows
about so it can vectorize operations on them.

That's an entirely separate discussion.

tuples right now try to fill too many roles and that makes life
difficult for both the compiler and the programmer. however, until then, we
need some kind of common, fixed layout currency type for vector data, and
by implementation-detail, *tuples are our best option* since for some
reason everyone is so opposed to the simple solution of baking vec2,
vec3, … vec5 into the language and the generic integer Vector<Float, k>
solution everyone wants likely isn’t going to materialize for the
foreseeable future.

Disagree. If you want a particular memory layout, define your type in C.
Swift's interop story is very good, and your type will have a fixed layout
forever.

“*write it in c and import it*” is *not* a solution,, it is a workaround.

Yes, that’s what we’re talking about here: workarounds to lack of a fixed
layout type. I’m saying C is the “best option” workaround, not tuples.

plus since it lives across the module boundary, it’s effectively opaque to

compiler optimizations.

What sorts of optimizations are you referring to? Recall that we are
talking about taking a value and unsafe bitcasting for the purposes of
calling other C APIs.

you misunderstand the use-case. C and C APIs usually don’t show up in the

See Thorsten’s response with, e.g.,

        Function<Double, InoutParam<String>, Param<Int>>

which handles “inout” by adding wrappers around the parameter types (which one would have to cope with in any user of Function), but still doesn’t handle argument labels. To handle argument labels, we would need something like strings as generic arguments. We’d also need to handle calling conventions and anything else we invent for function types.

  - Doug

···

On Nov 21, 2017, at 10:48 PM, David Hart <david@hartbit.com> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

What confuses me is that I always thought that T? was sugar for Optional<T> by design, and found that to be quite elegant. But now you’re telling me that its just a hack to allow conformance on Optionals until it can be made structural. I would have thought that it would be cleaner to have specific concepts (optionals, tuples, etc…) represented in terms of more general concepts (enum, struct) so that the compiler had less to reason about. I’m just trying to understand :-)

···

On 22 Nov 2017, at 07:48, David Hart via swift-evolution <swift-evolution@swift.org> wrote:
On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:
On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Do not forget the use case of typed units (e.g. 9.81 m/s^2) which was discussed long ago on this list where integers as generic parameters would store the exponent of each base unit.

-Thorsten

···

Am 23.11.2017 um 01:50 schrieb David Sweeris via swift-evolution <swift-evolution@swift.org>:

On Nov 21, 2017, at 22:54, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:48 PM, David Hart <david@hartbit.com> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

See Thorsten’s response with, e.g.,

        Function<Double, InoutParam<String>, Param<Int>>

which handles “inout” by adding wrappers around the parameter types (which one would have to cope with in any user of Function), but still doesn’t handle argument labels. To handle argument labels, we would need something like strings as generic arguments.

Oh, good! A use case for “literals as generic parameters” other than Vectors and Fixed-Size Arrays!

I would defend turning tuples into structs (change from structural type to
nominal type). This is a much better story for programmers, compare the two
stories:

   1. Tuples are just syntax sugar for simple structs.
   2. Tuples are sort of like structs but there is a list of things tuples
   can do that structs can't and a list of things structs can do and tuples
   can't.

I think unification can be achieved with some name mangling (Chris Lattner
noted this previously - I am just spelling out one scheme), e.g.:

// var a = (zero: 0, one: 1)
public struct Tuple_zero_Int_one_Int { // Mangle name.
    public var zero: Int
    public var one: Int
}
var a = Tuple_zero_Int_one_Int(zero: 0, one: 1)
// a.0 = -1
a.zero = -1

// var b = (0, 1)
public struct Tuple_0_Int_1_Int { // Mangle name.
    public var _0_: Int // Unique name.
    public var _1_: Int // Unique name.
}
var b = Tuple_0_Int_1_Int(_0_: 0, _1_: 1)
// a = b
a = Tuple_zero_Int_one_Int(zero: b._0_, one: b._1_)

Implicit in the above transformation is:

   1. struct and tuple have the same memory layout.
   2. `.0` access the 1st stored property of a struct, `.1` the 2nd, etc.

  -- Howard.

···

On 22 November 2017 at 18:02, David Hart via swift-evolution < swift-evolution@swift.org> wrote:

On 22 Nov 2017, at 07:54, Douglas Gregor <dgregor@apple.com> wrote:

On Nov 21, 2017, at 10:48 PM, David Hart <david@hartbit.com> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

Or alternatively, one could decide to make the generics system *only and
forever* work on nominal types, and make the syntactic sugar just be sugar
for named types like Swift.Tuple, Function, and Optional. Either design
could work.

We don’t have a way to make it work for function types, though, because of
parameter-passing conventions. Well, assuming we don’t invent something
that allows:

Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on
how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow
extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have
naturally thought that the language would be simpler as a whole if there
only existed nominal types and all structural types were just sugar over
them.

See Thorsten’s response with, e.g.,

      Function<Double, InoutParam<String>, Param<Int>>

which handles “inout” by adding wrappers around the parameter types (which
one would have to cope with in any user of Function), but still doesn’t
handle argument labels. To handle argument labels, we would need something
like strings as generic arguments. We’d also need to handle calling
conventions and anything else we invent for function types.

Oh ok, I get. The ugliness comes from trying to shoehorn structural types
into nominal types.

- Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

You don't necessarily need variadic generics to enable tuples to be nominal
types. Though you do need some changes to the generic type system. One
possibility is:

struct Tuple_0_Int_1_Int<T/*, T0, T1*/>: Collection /*where T0: T, T1: T*/
{ // Mangle name. Need covariant generics to enable constraint of T0 and T1.
    typealias Element = T
    typealias Index = Int
    typealias SubSequence = Slice<Tuple_0_Int_1_Int<T/*, T0, T1*/>>
    var _0_: T/*0*/ // Unique name.
    var _1_: T/*1*/ // Unique name.
    let startIndex: Int = 0
    let endIndex: Int = 2
    subscript(position: Int) -> T {
        switch position {
        case 0: return _0_
        case 1: return _1_
        default: fatalError("Index out of bounds (must be 0 or 1).")
        }
    }
    func index(after i: Int) -> Int {
        return i + 1
    }
}

// Ideally:
// 1. Extension on Tuple_0_Int_1_Int (SE-143).
// 2. Collection extension rather than on every Tuple (SE-143 extension).
func == <T>(lhs: Tuple_0_Int_1_Int<T>, rhs: Tuple_0_Int_1_Int<T>) -> Bool
where T: Equatable {
    let size = lhs.count
    guard size == rhs.count else {
        return false
    }
    for i in 0 ..< size {
        guard lhs[i] == rhs[i] else {
            return false
        }
    }
    return true
}

The above is valid Swift 4, but I really want to be able to say:

   1. `struct Tuple_0_Int_1_Int<T, T0, T1>: Collection where T0: T, T1: T`,
   i.e. covariant generics.
   2. Ideally SE143+ also, so that I can extend Collection to implement
   Equatable, Hashable, etc.

Note:

   1. Tuples extend Collection, not MutableCollection.
   2. T is the base type of T0 and T1.
   3. A Collection of T is sufficient to write interesting generic
   algorithms; equals, etc.

  -- Howard.

···

On 23 November 2017 at 11:50, David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 22:54, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:48 PM, David Hart <david@hartbit.com> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

Or alternatively, one could decide to make the generics system *only and
forever* work on nominal types, and make the syntactic sugar just be sugar
for named types like Swift.Tuple, Function, and Optional. Either design
could work.

We don’t have a way to make it work for function types, though, because of
parameter-passing conventions. Well, assuming we don’t invent something
that allows:

Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on
how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow
extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have
naturally thought that the language would be simpler as a whole if there
only existed nominal types and all structural types were just sugar over
them.

See Thorsten’s response with, e.g.,

      Function<Double, InoutParam<String>, Param<Int>>

which handles “inout” by adding wrappers around the parameter types (which
one would have to cope with in any user of Function), but still doesn’t
handle argument labels. To handle argument labels, we would need something
like strings as generic arguments.

Oh, good! A use case for “literals as generic parameters” *other* than
Vectors and Fixed-Size Arrays!

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Don't worry too much about it. The people in this thread are conflating a lot of different things, including some people who ought to know better. Your way of looking at it is perfectly accurate.

A fairly standard formalization of types is that you have these type expressions, which, in a simple system, are either type constants or type applications.

A type constant is something like Int, Float, or Array. struct/enum/class declarations in Swift all introduce new type constants, where each declaration has its own identity as a type. Since we don't allow type names to be redeclared in a scope, the identity of such declared types can be uniquely determined by (1) the type's name in its scope and (2) the path to that scope. Hence the identity is "by name", or "nominal".

A type application takes a generic type and applies it at some number of arguments. The general syntax for this in Swift is <>. For example, Array is a generic type, and Array<Int> is a type application of the generic type Array with the argument Int. The arguments can be arbitrary type expressions, e.g. Dictionary<String, Array<Float>>. The identity of a type application is determined purely by its applicative structure: first, the identity of the generic type being applied, and second, the identity of the type arguments it is being applied to. That is, A<B> is the same type as C<D> as long as the type-expression A is the same generic type as C and B is the same type as D. Hence the identity is "structural".

(Formal nitpick: we're making some assumptions about the nature of type expressions that generally work out until you get into really advanced type systems.)

What is a tuple type in this formalization? Well, the rule we want is that (A, B) is the same type as (C, D) if A is identical to C and B is identical to D. We could add this as a special new rule to our definition of type expressions above, but we don't really need to, because it turns out that it's exactly the same as if we had some sort of tuple type constant — for sake of argument, let's call it (...) — and allowed it to be applied to an arbitrary number of types. Swift does not actually allow you to name this as an unapplied generic type — you cannot write (...)<Int, Float> instead of (Int, Float) — but that's just a matter of syntax. The fact that (...) is built into the language rather than being declared in the standard library as Tuple is not fundamentally significant. Nor is it fundamentally significant for Optional, which is currently declared in the standard library instead of being built-in.

(Function types in Swift are weird because, as covered previously in this thread, there's more to their structure than just component type identity. You could still stretch the formalization to make this work, but, well, they'd still be weird.)

Now, function, tuple, and optional types are special in the language in several ways: there are primitive declarations and expressions that introduce or manipulate these types, and there are special rules in the type system for them. (For example, optionals and tuples obey covariant subtyping.) But we could technically do the same for any type if we wanted to. We could even do it to arbitrary third-party types as long as we handled the possibility that they weren't imported into the current program.

We could absolutely allow tuples to conform to protocols without first introducing variadic generics. We'd have to solve some straightforward internal representation problems, and then we'd just need some way of spelling them (if we didn't just hard-code a few conformances as special).

John.

···

On Nov 22, 2017, at 2:01 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 22 Nov 2017, at 07:48, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

What confuses me is that I always thought that T? was sugar for Optional<T> by design, and found that to be quite elegant. But now you’re telling me that its just a hack to allow conformance on Optionals until it can be made structural. I would have thought that it would be cleaner to have specific concepts (optionals, tuples, etc…) represented in terms of more general concepts (enum, struct) so that the compiler had less to reason about. I’m just trying to understand :-)

1 Like

Thanks for the lengthy explanation! I really appreciate the time taken to educate.

When you say that we could allow tuples to conform to protocols without first introducing Variadic Generics, it would still require some new syntax though? Is that a complexity cost that would be reason enough to wait for the general solution of Variadic Generics? Here, when I mention complexity, I’m talking about the complexity of the language, not it’s implementation.

···

On 27 Nov 2017, at 07:32, John McCall <rjmccall@apple.com> wrote:

On Nov 22, 2017, at 2:01 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 22 Nov 2017, at 07:48, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

What confuses me is that I always thought that T? was sugar for Optional<T> by design, and found that to be quite elegant. But now you’re telling me that its just a hack to allow conformance on Optionals until it can be made structural. I would have thought that it would be cleaner to have specific concepts (optionals, tuples, etc…) represented in terms of more general concepts (enum, struct) so that the compiler had less to reason about. I’m just trying to understand :-)

Don't worry too much about it. The people in this thread are conflating a lot of different things, including some people who ought to know better. Your way of looking at it is perfectly accurate.

A fairly standard formalization of types is that you have these type expressions, which, in a simple system, are either type constants or type applications.

A type constant is something like Int, Float, or Array. struct/enum/class declarations in Swift all introduce new type constants, where each declaration has its own identity as a type. Since we don't allow type names to be redeclared in a scope, the identity of such declared types can be uniquely determined by (1) the type's name in its scope and (2) the path to that scope. Hence the identity is "by name", or "nominal".

A type application takes a generic type and applies it at some number of arguments. The general syntax for this in Swift is <>. For example, Array is a generic type, and Array<Int> is a type application of the generic type Array with the argument Int. The arguments can be arbitrary type expressions, e.g. Dictionary<String, Array<Float>>. The identity of a type application is determined purely by its applicative structure: first, the identity of the generic type being applied, and second, the identity of the type arguments it is being applied to. That is, A<B> is the same type as C<D> as long as the type-expression A is the same generic type as C and B is the same type as D. Hence the identity is "structural".

(Formal nitpick: we're making some assumptions about the nature of type expressions that generally work out until you get into really advanced type systems.)

What is a tuple type in this formalization? Well, the rule we want is that (A, B) is the same type as (C, D) if A is identical to C and B is identical to D. We could add this as a special new rule to our definition of type expressions above, but we don't really need to, because it turns out that it's exactly the same as if we had some sort of tuple type constant — for sake of argument, let's call it (...) — and allowed it to be applied to an arbitrary number of types. Swift does not actually allow you to name this as an unapplied generic type — you cannot write (...)<Int, Float> instead of (Int, Float) — but that's just a matter of syntax. The fact that (...) is built into the language rather than being declared in the standard library as Tuple is not fundamentally significant. Nor is it fundamentally significant for Optional, which is currently declared in the standard library instead of being built-in.

(Function types in Swift are weird because, as covered previously in this thread, there's more to their structure than just component type identity. You could still stretch the formalization to make this work, but, well, they'd still be weird.)

Now, function, tuple, and optional types are special in the language in several ways: there are primitive declarations and expressions that introduce or manipulate these types, and there are special rules in the type system for them. (For example, optionals and tuples obey covariant subtyping.) But we could technically do the same for any type if we wanted to. We could even do it to arbitrary third-party types as long as we handled the possibility that they weren't imported into the current program.

We could absolutely allow tuples to conform to protocols without first introducing variadic generics. We'd have to solve some straightforward internal representation problems, and then we'd just need some way of spelling them (if we didn't just hard-code a few conformances as special).

John.

I think it would be difficult to allow useful user-defined extensions/conformances of arbitrary tuple types without variadic generics. I think we could probably allow fixed-arity extensions/conformances more easily, but we'd run some complexity risk by encouraging people to make a bunch of conformances at different arities. Either would require some substantial implementation effort, mostly around name lookup, which is currently heavily declaration-centric.

John.

···

On Nov 27, 2017, at 1:43 AM, David Hart <david@hartbit.com> wrote:
On 27 Nov 2017, at 07:32, John McCall <rjmccall@apple.com <mailto:rjmccall@apple.com>> wrote:

On Nov 22, 2017, at 2:01 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 22 Nov 2017, at 07:48, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 22 Nov 2017, at 07:41, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 21, 2017, at 10:37 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:

On Nov 21, 2017, at 9:25 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

Or alternatively, one could decide to make the generics system *only and forever* work on nominal types, and make the syntactic sugar just be sugar for named types like Swift.Tuple, Function, and Optional. Either design could work.

We don’t have a way to make it work for function types, though, because of parameter-passing conventions. Well, assuming we don’t invent something that allows:

  Function<Double, inout String>

to exist in the type system. Tuple labels have a similar problem.

I’m totally aware of that and mentioned it upthread.

Eh, sorry I missed it.

There are various encoding tricks that could make this work depending on how you want to stretch the current generics system…

I think it’s straightforward and less ugly to make structural types allow extensions and protocol conformances.

Can somebody explain to me what is less ugly about that? I would have naturally thought that the language would be simpler as a whole if there only existed nominal types and all structural types were just sugar over them.

What confuses me is that I always thought that T? was sugar for Optional<T> by design, and found that to be quite elegant. But now you’re telling me that its just a hack to allow conformance on Optionals until it can be made structural. I would have thought that it would be cleaner to have specific concepts (optionals, tuples, etc…) represented in terms of more general concepts (enum, struct) so that the compiler had less to reason about. I’m just trying to understand :-)

Don't worry too much about it. The people in this thread are conflating a lot of different things, including some people who ought to know better. Your way of looking at it is perfectly accurate.

A fairly standard formalization of types is that you have these type expressions, which, in a simple system, are either type constants or type applications.

A type constant is something like Int, Float, or Array. struct/enum/class declarations in Swift all introduce new type constants, where each declaration has its own identity as a type. Since we don't allow type names to be redeclared in a scope, the identity of such declared types can be uniquely determined by (1) the type's name in its scope and (2) the path to that scope. Hence the identity is "by name", or "nominal".

A type application takes a generic type and applies it at some number of arguments. The general syntax for this in Swift is <>. For example, Array is a generic type, and Array<Int> is a type application of the generic type Array with the argument Int. The arguments can be arbitrary type expressions, e.g. Dictionary<String, Array<Float>>. The identity of a type application is determined purely by its applicative structure: first, the identity of the generic type being applied, and second, the identity of the type arguments it is being applied to. That is, A<B> is the same type as C<D> as long as the type-expression A is the same generic type as C and B is the same type as D. Hence the identity is "structural".

(Formal nitpick: we're making some assumptions about the nature of type expressions that generally work out until you get into really advanced type systems.)

What is a tuple type in this formalization? Well, the rule we want is that (A, B) is the same type as (C, D) if A is identical to C and B is identical to D. We could add this as a special new rule to our definition of type expressions above, but we don't really need to, because it turns out that it's exactly the same as if we had some sort of tuple type constant — for sake of argument, let's call it (...) — and allowed it to be applied to an arbitrary number of types. Swift does not actually allow you to name this as an unapplied generic type — you cannot write (...)<Int, Float> instead of (Int, Float) — but that's just a matter of syntax. The fact that (...) is built into the language rather than being declared in the standard library as Tuple is not fundamentally significant. Nor is it fundamentally significant for Optional, which is currently declared in the standard library instead of being built-in.

(Function types in Swift are weird because, as covered previously in this thread, there's more to their structure than just component type identity. You could still stretch the formalization to make this work, but, well, they'd still be weird.)

Now, function, tuple, and optional types are special in the language in several ways: there are primitive declarations and expressions that introduce or manipulate these types, and there are special rules in the type system for them. (For example, optionals and tuples obey covariant subtyping.) But we could technically do the same for any type if we wanted to. We could even do it to arbitrary third-party types as long as we handled the possibility that they weren't imported into the current program.

We could absolutely allow tuples to conform to protocols without first introducing variadic generics. We'd have to solve some straightforward internal representation problems, and then we'd just need some way of spelling them (if we didn't just hard-code a few conformances as special).

John.

Thanks for the lengthy explanation! I really appreciate the time taken to educate.

When you say that we could allow tuples to conform to protocols without first introducing Variadic Generics, it would still require some new syntax though? Is that a complexity cost that would be reason enough to wait for the general solution of Variadic Generics? Here, when I mention complexity, I’m talking about the complexity of the language, not it’s implementation.