Idea: Let Generic Parameters Have Labels & Default Values

The title seems fairly self-explanatory. Does anyone else think this could be useful?
struct True : BooleanType {…} // Just part of the example… not in the proposal (although I do like it)
struct False : BooleanType {…} // Same
// This is where the actual idea starts
struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

The first parameter label could be skipped (or not), depending on whatever the rules for functions parameter labels ends up being (either way, they should be the same IMHO). Then variables could be declared like this:
let foo = BigInt() // BigInt<Int, No>()
let bar = BigInt<Int32>() // For when your data will be processed on a 32-bit platform or something
let divisor = BigInt<CanEqualZero: False>()

(The obvious follow-up suggestion is to then allow a generic type’s definition to change based on the results of logical operations performed purely on the types that are passed in, but I think that’s getting into “macro system” territory, and should probably be its own thing.)

Anyway, thoughts?

- Dave Sweeris

+1

···

on Sat Jan 23 2016, David Sweeris <swift-evolution@swift.org> wrote:

The title seems fairly self-explanatory. Does anyone else think this could be useful?
struct True : BooleanType {…} // Just part of the example… not in the proposal (although I do like it)
struct False : BooleanType {…} // Same
// This is where the actual idea starts
struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

The first parameter label could be skipped (or not), depending on
whatever the rules for functions parameter labels ends up being
(either way, they should be the same IMHO). Then variables could be
declared like this:
let foo = BigInt() // BigInt<Int, No>()
let bar = BigInt<Int32>() // For when your data will be processed on a 32-bit platform or something
let divisor = BigInt<CanEqualZero: False>()

(The obvious follow-up suggestion is to then allow a generic type’s
definition to change based on the results of logical operations
performed purely on the types that are passed in, but I think that’s
getting into “macro system” territory, and should probably be its own
thing.)

Anyway, thoughts?

--
-Dave

Hi David,

struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

Is the "Yes" an error and it should be "True" instead?

The first parameter label could be skipped (or not), depending on whatever the rules for functions parameter labels ends up being (either way, they should be the same IMHO).

I don't think the rules have to be identical:
Methods are often verbs, so often, there is a natural "target" whose meaning can be inferred from the method name.
Generics are different — they "don't tell a story", and although there are situations where it seems one parameter is more relevant than the others ("BaseType" in you example), it would be odd to declare "BigIntWithBaseType<…".

I guess when there is a single parameter, its meaning is clear in most cases, but when you have more parameters, you cannot assume that the first should be treated different from the second (neither would it make sense to always treat them in the same way.

Every label useful:
let vector: Map<Index = UInt, Content = Double>

No label useful:
class Comparer<L: BooleanType, R: BooleanType>

It's to hard for the compiler to decide what should be done, and in such situations, my lazy solution is always "leave the decision to the user".

I guess I'm to slow building my stupid litte proposal — but even if I haven't written an example for instantiation yet, I came to the same conclusion that labels can be useful for generics (Home · SwiftInofficialEvolution/Home Wiki · GitHub).
Your use case might benefit from it as well: afaics, "True" and "False" are only there to "lift" true and false into type-space, qualifying them as parameters.

Please feel free to comment or even co-author on "compile-time parameters" — I'm not sure I'll be bold enough to turn it into a real pull-request without external pressure ;-)
(afair, you already posted in the thread ["typesafe calculations"])

Tino

I’m not saying default values should be required, just that they be allowed if it makes sense. Plus, the way Array is defined, its generic parameter can always be inferred. In your example, `a` would be an array of IntergerLiteralType (which is currently typealiased to Int). However, it’s perfectly legal to define a struct for which the compiler can’t always infer all the generic parameters
struct Foo <T, U> {
    var value: T
    var opt2ndValue: U?
    init(_ value: T, opt2ndValue: U? = nil) {
        self.value = value
        self.opt2ndValue = opt2ndValue
    }
}
In which case, the following code won’t compile:
let foo = Foo(bar)
because U can’t be inferred. The current workaround:
let foo = Foo<Int, Whatever>(bar)
requires you to specify all the types, which means that bar cannot be an inferred type, because this isn’t valid:
let foo = Foo<bar.dynamicType, Whatever>(bar)

Default values for generic parameters let you maintain type inference, if it would make sense in your case. If it doesn’t, then don’t provide one.

- Dave Sweeris

···

On Jan 25, 2016, at 01:00, Howard Lovatt <howard.lovatt@gmail.com> wrote:

+1 for labels, I think generics are special arguments to `inits` and therefore labels make sense to me.

-1 for default values, the value is inferred if not specifically specified. What would:

    let a = [1]

be? An `Int` array or an array of whatever the default generic type for an array is?

Sent from my iPad

On 24 Jan 2016, at 7:38 AM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The title seems fairly self-explanatory. Does anyone else think this could be useful?
struct True : BooleanType {…} // Just part of the example… not in the proposal (although I do like it)
struct False : BooleanType {…} // Same
// This is where the actual idea starts
struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

The first parameter label could be skipped (or not), depending on whatever the rules for functions parameter labels ends up being (either way, they should be the same IMHO). Then variables could be declared like this:
let foo = BigInt() // BigInt<Int, No>()
let bar = BigInt<Int32>() // For when your data will be processed on a 32-bit platform or something
let divisor = BigInt<CanEqualZero: False>()

(The obvious follow-up suggestion is to then allow a generic type’s definition to change based on the results of logical operations performed purely on the types that are passed in, but I think that’s getting into “macro system” territory, and should probably be its own thing.)

Anyway, thoughts?

- Dave Sweeris

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

Response inlined...

Hi David,

struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

Is the "Yes" an error and it should be "True" instead?

Yep. I guess that’s what I get for changing my mind half-way through writing something :-)

The first parameter label could be skipped (or not), depending on whatever the rules for functions parameter labels ends up being (either way, they should be the same IMHO).

I don't think the rules have to be identical:
Methods are often verbs, so often, there is a natural "target" whose meaning can be inferred from the method name.
Generics are different — they "don't tell a story", and although there are situations where it seems one parameter is more relevant than the others ("BaseType" in you example), it would be odd to declare "BigIntWithBaseType<…".

I guess when there is a single parameter, its meaning is clear in most cases, but when you have more parameters, you cannot assume that the first should be treated different from the second (neither would it make sense to always treat them in the same way.

Every label useful:
let vector: Map<Index = UInt, Content = Double>

No label useful:
class Comparer<L: BooleanType, R: BooleanType>

It's to hard for the compiler to decide what should be done, and in such situations, my lazy solution is always "leave the decision to the user".

I guess I hadn't enough thought to the formal syntax. What I'd originally posted had a lot of colons by the time it was all typed out. Should we just copy the syntax from function arguments? So the external label would default to being the same as the parameter name, unless you put a "_" in front of it or provided a different one? Seems like a waste to come up with two different syntaxes for two concepts that are so similar (plus, I think of generics as "compile-time arguments" anyway).

Either way, I think parameters with default types would need to also have an external label, for the same reason that function arguments with default values can't externally be "_".

And, as Andrew suggested, anything with an explicit external label gets implicitly typealiased.

I guess I'm to slow building my stupid litte proposal — but even if I haven't written an example for instantiation yet, I came to the same conclusion that labels can be useful for generics (https://github.com/SwiftTypesafeCalculations/Home/wiki/compile-time%20parameters\).
Your use case might benefit from it as well: affairs, "True" and "False" are only there to "lift" true and false into type-space, qualifying them as parameters.
Please feel free to comment or even co-author on "compile-time parameters" — I'm not sure I'll be bold enough to turn it into a real pull-request without external pressure ;-)
(afair, you already posted in the thread ["typesafe calculations"])

Tino

Yeah, if that goes through (I really hope it does), those True/False types wouldn't be needed.

I'll take a closer look at your formal proposal as soon as I get a chance and let you know if I think I can contribute to it :-)

- Dave Sweeris

···

On Jan 24, 2016, at 03:08, Tino Heth <2th@gmx.de> wrote:

I like the idea.

I think I'd be using typealias a lot to make things more concise, but
that's not necessarily a problem.

How would I name the following parameter?

    struct OrderedCollection<T: Comparable> {
    }

As I understand it, would I do it as follows?

    struct OrderedCollection<Element: T: Comparable> {
    }

Or would all type constraints now require `where`, like this:
    struct OrderedCollection<Element: T where T: Comparable> {
    }

Also, a typealias seems somewhat redundant when the parameter is labelled:

    struct OrderedCollection<Element: T where T: Comparable> {
        typealias Element = T
    }

I wonder if the typealias could be implied.

···

On Sun, Jan 24, 2016 at 9:52 AM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Sat Jan 23 2016, David Sweeris <swift-evolution@swift.org> wrote:

> The title seems fairly self-explanatory. Does anyone else think this
could be useful?
> struct True : BooleanType {…} // Just part of the example… not in the
proposal (although I do like it)
> struct False : BooleanType {…} // Same
> // This is where the actual idea starts
> struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T:
IntegerArithmeticType, U: BooleanType> {…}
>
> The first parameter label could be skipped (or not), depending on
> whatever the rules for functions parameter labels ends up being
> (either way, they should be the same IMHO). Then variables could be
> declared like this:
> let foo = BigInt() // BigInt<Int, No>()
> let bar = BigInt<Int32>() // For when your data will be processed on a
32-bit platform or something
> let divisor = BigInt<CanEqualZero: False>()
>
> (The obvious follow-up suggestion is to then allow a generic type’s
> definition to change based on the results of logical operations
> performed purely on the types that are passed in, but I think that’s
> getting into “macro system” territory, and should probably be its own
> thing.)
>
> Anyway, thoughts?
>

+1
--
-Dave

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

+1

···

On Mon, Jan 25, 2016 at 2:44 PM, Dave via swift-evolution < swift-evolution@swift.org> wrote:

I’m not saying default values should be *required*, just that they be
allowed if it makes sense. Plus, the way Array is defined, its generic
parameter can always be inferred. In your example, `a` would be an array of
IntergerLiteralType (which is currently typealiased to Int). However, it’s
perfectly legal to define a struct for which the compiler can’t always
infer all the generic parameters
struct Foo <T, U> {
    var value: T
    var opt2ndValue: U?
    init(_ value: T, opt2ndValue: U? = nil) {
        self.value = value
        self.opt2ndValue = opt2ndValue
    }
}
In which case, the following code won’t compile:
let foo = Foo(bar)
because U can’t be inferred. The current workaround:
let foo = Foo<Int, Whatever>(bar)
requires you to specify *all* the types, which means that bar cannot be
an inferred type, because this isn’t valid:
let foo = Foo<bar.dynamicType, Whatever>(bar)

Default values for generic parameters let you maintain type inference,
*if* it would make sense in your case. If it doesn’t, then don’t provide
one.

- Dave Sweeris

On Jan 25, 2016, at 01:00, Howard Lovatt <howard.lovatt@gmail.com> wrote:

+1 for labels, I think generics are special arguments to `inits` and
therefore labels make sense to me.

-1 for default values, the value is inferred if not specifically
specified. What would:

    let a = [1]

be? An `Int` array or an array of whatever the default generic type for an
array is?

Sent from my iPad

On 24 Jan 2016, at 7:38 AM, David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

The title seems fairly self-explanatory. Does anyone else think this could
be useful?
struct True : BooleanType {…} // Just part of the example… not in the
proposal (although I do like it)
struct False : BooleanType {…} // Same
// This is where the actual idea starts
struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T:
IntegerArithmeticType, U: BooleanType> {…}

The first parameter label could be skipped (or not), depending on whatever
the rules for functions parameter labels ends up being (either way, they
should be the same IMHO). Then variables could be declared like this:
let foo = BigInt() // BigInt<Int, No>()
let bar = BigInt<Int32>() // For when your data will be processed on a
32-bit platform or something
let divisor = BigInt<CanEqualZero: False>()

(The obvious follow-up suggestion is to then allow a generic type’s
definition to change based on the results of logical operations performed
purely on the types that are passed in, but I think that’s getting into
“macro system” territory, and should probably be its own thing.)

Anyway, thoughts?

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
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

In another thread:

[swift-evolution] Partially Constrained Protocols [Was: [Proposal] Separate protocols and interfaces]

This problem (and many others) could be solved with:

        let foo = Foo<where Self.U == Whatever>(bar)

Where `T` is inferred to be of type `Int`.

While this example is pretty verbose this kind of syntax can be used to use protocols with associated types as variables and generic covariant types.

+1 for having label like behavior but they shouldn't be explicit since there could be confusion whether you declare a generic type or only a label. Therefore implicit typealiases (in this case `U` and `T`) should be used in conjunction with the approach above.

-1/+1 for default generic types because I imagine them to be useful in some (special) cases but I couldn't think of any useful real problem. So the additional syntax could not be worth its benefit.

Best regards
- Maximilian

···

Am 25.01.2016 um 20:44 schrieb Dave via swift-evolution <swift-evolution@swift.org>:

I’m not saying default values should be required, just that they be allowed if it makes sense. Plus, the way Array is defined, its generic parameter can always be inferred. In your example, `a` would be an array of IntergerLiteralType (which is currently typealiased to Int). However, it’s perfectly legal to define a struct for which the compiler can’t always infer all the generic parameters
struct Foo <T, U> {
    var value: T
    var opt2ndValue: U?
    init(_ value: T, opt2ndValue: U? = nil) {
        self.value = value
        self.opt2ndValue = opt2ndValue
    }
}
In which case, the following code won’t compile:
let foo = Foo(bar)
because U can’t be inferred. The current workaround:
let foo = Foo<Int, Whatever>(bar)
requires you to specify all the types, which means that bar cannot be an inferred type, because this isn’t valid:
let foo = Foo<bar.dynamicType, Whatever>(bar)

Default values for generic parameters let you maintain type inference, if it would make sense in your case. If it doesn’t, then don’t provide one.

- Dave Sweeris

On Jan 25, 2016, at 01:00, Howard Lovatt <howard.lovatt@gmail.com> wrote:

+1 for labels, I think generics are special arguments to `inits` and therefore labels make sense to me.

-1 for default values, the value is inferred if not specifically specified. What would:

    let a = [1]

be? An `Int` array or an array of whatever the default generic type for an array is?

Sent from my iPad

On 24 Jan 2016, at 7:38 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

The title seems fairly self-explanatory. Does anyone else think this could be useful?
struct True : BooleanType {…} // Just part of the example… not in the proposal (although I do like it)
struct False : BooleanType {…} // Same
// This is where the actual idea starts
struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

The first parameter label could be skipped (or not), depending on whatever the rules for functions parameter labels ends up being (either way, they should be the same IMHO). Then variables could be declared like this:
let foo = BigInt() // BigInt<Int, No>()
let bar = BigInt<Int32>() // For when your data will be processed on a 32-bit platform or something
let divisor = BigInt<CanEqualZero: False>()

(The obvious follow-up suggestion is to then allow a generic type’s definition to change based on the results of logical operations performed purely on the types that are passed in, but I think that’s getting into “macro system” territory, and should probably be its own thing.)

Anyway, thoughts?

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
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

I think it’d more readable to move type constraints to the where clause, but I don’t know if it needs to be a requirement.

I really like your implicit typealias idea…
struct Foo<Element: T, _: U where T: Comparable> {
    // implicit typealias Element = T
    typealias Bar = U // not implicit because U didn’t have a label
}

···

On Jan 23, 2016, at 19:18, Andrew Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I like the idea.

I think I'd be using typealias a lot to make things more concise, but that's not necessarily a problem.

How would I name the following parameter?

    struct OrderedCollection<T: Comparable> {
    }

As I understand it, would I do it as follows?

    struct OrderedCollection<Element: T: Comparable> {
    }

Or would all type constraints now require `where`, like this:
    struct OrderedCollection<Element: T where T: Comparable> {
    }

Also, a typealias seems somewhat redundant when the parameter is labelled:

    struct OrderedCollection<Element: T where T: Comparable> {
        typealias Element = T
    }

I wonder if the typealias could be implied.

On Sun, Jan 24, 2016 at 9:52 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

on Sat Jan 23 2016, David Sweeris <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> The title seems fairly self-explanatory. Does anyone else think this could be useful?
> struct True : BooleanType {…} // Just part of the example… not in the proposal (although I do like it)
> struct False : BooleanType {…} // Same
> // This is where the actual idea starts
> struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}
>
> The first parameter label could be skipped (or not), depending on
> whatever the rules for functions parameter labels ends up being
> (either way, they should be the same IMHO). Then variables could be
> declared like this:
> let foo = BigInt() // BigInt<Int, No>()
> let bar = BigInt<Int32>() // For when your data will be processed on a 32-bit platform or something
> let divisor = BigInt<CanEqualZero: False>()
>
> (The obvious follow-up suggestion is to then allow a generic type’s
> definition to change based on the results of logical operations
> performed purely on the types that are passed in, but I think that’s
> getting into “macro system” territory, and should probably be its own
> thing.)
>
> Anyway, thoughts?
>

+1
--
-Dave

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Without commenting on anything else, IIRC we were already planning to make the local typealias implied, even for today's generic parameters.

Jordan

···

On Jan 23, 2016, at 19:18, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

I like the idea.

I think I'd be using typealias a lot to make things more concise, but that's not necessarily a problem.

How would I name the following parameter?

    struct OrderedCollection<T: Comparable> {
    }

As I understand it, would I do it as follows?

    struct OrderedCollection<Element: T: Comparable> {
    }

Or would all type constraints now require `where`, like this:
    struct OrderedCollection<Element: T where T: Comparable> {
    }

Also, a typealias seems somewhat redundant when the parameter is labelled:

    struct OrderedCollection<Element: T where T: Comparable> {
        typealias Element = T
    }

I wonder if the typealias could be implied.

This is the 2nd time someone has pointed out that this, at least AFAICT, provides related functionality to parts of other proposals. It seems counterproductive to have multiple proposals out there with conflicting syntax for the same thing, especially when the different syntaxes could potentially have different corner-cases, which could be important to other parts of the proposal. Is there a “standard” way to make sure this isn’t stepping on anyone’s toes?

- Dave Sweeris

···

On Jan 25, 2016, at 15:03, Maximilian Hünenberger <m.huenenberger@me.com> wrote:

In another thread:

[swift-evolution] Partially Constrained Protocols [Was: [Proposal] Separate protocols and interfaces]

This problem (and many others) could be solved with:

        let foo = Foo<where Self.U == Whatever>(bar)

Where `T` is inferred to be of type `Int`.

While this example is pretty verbose this kind of syntax can be used to use protocols with associated types as variables and generic covariant types.

+1 for having label like behavior but they shouldn't be explicit since there could be confusion whether you declare a generic type or only a label. Therefore implicit typealiases (in this case `U` and `T`) should be used in conjunction with the approach above.

-1/+1 for default generic types because I imagine them to be useful in some (special) cases but I couldn't think of any useful real problem. So the additional syntax could not be worth its benefit.

Best regards
- Maximilian

Am 25.01.2016 um 20:44 schrieb Dave via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

I’m not saying default values should be required, just that they be allowed if it makes sense. Plus, the way Array is defined, its generic parameter can always be inferred. In your example, `a` would be an array of IntergerLiteralType (which is currently typealiased to Int). However, it’s perfectly legal to define a struct for which the compiler can’t always infer all the generic parameters
struct Foo <T, U> {
    var value: T
    var opt2ndValue: U?
    init(_ value: T, opt2ndValue: U? = nil) {
        self.value = value
        self.opt2ndValue = opt2ndValue
    }
}
In which case, the following code won’t compile:
let foo = Foo(bar)
because U can’t be inferred. The current workaround:
let foo = Foo<Int, Whatever>(bar)
requires you to specify all the types, which means that bar cannot be an inferred type, because this isn’t valid:
let foo = Foo<bar.dynamicType, Whatever>(bar)

Default values for generic parameters let you maintain type inference, if it would make sense in your case. If it doesn’t, then don’t provide one.

- Dave Sweeris

On Jan 25, 2016, at 01:00, Howard Lovatt <howard.lovatt@gmail.com <mailto:howard.lovatt@gmail.com>> wrote:

+1 for labels, I think generics are special arguments to `inits` and therefore labels make sense to me.

-1 for default values, the value is inferred if not specifically specified. What would:

    let a = [1]

be? An `Int` array or an array of whatever the default generic type for an array is?

Sent from my iPad

On 24 Jan 2016, at 7:38 AM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The title seems fairly self-explanatory. Does anyone else think this could be useful?
struct True : BooleanType {…} // Just part of the example… not in the proposal (although I do like it)
struct False : BooleanType {…} // Same
// This is where the actual idea starts
struct BigInt <BaseType: T = Int, CanEqualZero: U = Yes where T: IntegerArithmeticType, U: BooleanType> {…}

The first parameter label could be skipped (or not), depending on whatever the rules for functions parameter labels ends up being (either way, they should be the same IMHO). Then variables could be declared like this:
let foo = BigInt() // BigInt<Int, No>()
let bar = BigInt<Int32>() // For when your data will be processed on a 32-bit platform or something
let divisor = BigInt<CanEqualZero: False>()

(The obvious follow-up suggestion is to then allow a generic type’s definition to change based on the results of logical operations performed purely on the types that are passed in, but I think that’s getting into “macro system” territory, and should probably be its own thing.)

Anyway, thoughts?

- Dave Sweeris

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution