Default Generic Arguments


(Srđan Rašić) #1

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing
default generic arguments which I think would be nice addition to the
language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not
coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.

It would be great to hear your opinions and suggestions so I can refine the
proposal.


(Douglas Gregor) #2

The proposal looks fairly straightforward and reasonable. One thing to think about is how it interacts with type inference. For example, consider these examples:

  struct X<T = Int> { }

  func f1() -> X<Double> { return X() }

  func f2() -> X<Int> { return X() }
  func f2() -> X<Double> { return X() }

  func f3<T>(_: T) -> X<T> { return X() }

  let x1: X = f1() // okay: x1 has type X<Double>?
  let x2: X = f2() // ambiguous?
  let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
  let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a particular type, fill in a default” that is used for literals. That rule could be used here… or we could do something else. This should be discussed in the proposal.

Thanks for working on this!

  - Doug

···

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution <swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing default generic arguments which I think would be nice addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not coved by the proposal and I don't think that we need default generic arguments in generic functions as all the types are always part of the function signature so the compiler can always infer them. One corner case might be if using default argument values in which case support for default generic arguments in functions might be useful.


(Joe Groff) #3

There's an interesting parallel to the default behavior of literals. The type of a number or string literal is inferred from type context, or falls back to a default type like Int or String if that doesn't come up with an answer. You could think of that of saying the 'Self' type of the protocol constraint has a default (and maybe that's how we'd generalize the "default type for a protocol" feature if we wanted to.) It makes sense to me to follow a similar model for generic parameter defaults; that way, there's one consistent rule that applies.

-Joe

···

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing default generic arguments which I think would be nice addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not coved by the proposal and I don't think that we need default generic arguments in generic functions as all the types are always part of the function signature so the compiler can always infer them. One corner case might be if using default argument values in which case support for default generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to think about is how it interacts with type inference. For example, consider these examples:

  struct X<T = Int> { }

  func f1() -> X<Double> { return X() }

  func f2() -> X<Int> { return X() }
  func f2() -> X<Double> { return X() }

  func f3<T>(_: T) -> X<T> { return X() }

  let x1: X = f1() // okay: x1 has type X<Double>?
  let x2: X = f2() // ambiguous?
  let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
  let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a particular type, fill in a default” that is used for literals. That rule could be used here… or we could do something else. This should be discussed in the proposal.

Thanks for working on this!


(Michael Ilseman) #4

The proposal looks good to me with one possible concern. I'm leaning toward types that use the defaults should still require the angle brackets, X<>. This makes it clear that you're using a generic type.

What are the perceived benefits by making it explicit that you’re using a defaulted-or-inferred generic type? What important pieces of information would the presence of an explicit “<>” communicate to future readers/maintainers of the code?

···

On Jan 23, 2017, at 10:41 AM, Trent Nadeau via swift-evolution <swift-evolution@swift.org> wrote:

That leads me to think that the examples Doug gave should be an error as the explicit types on the `let`s should either be omitted completely or fully specified (as X<>, X<Double>, X<Int>, etc.).

On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing default generic arguments which I think would be nice addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not coved by the proposal and I don't think that we need default generic arguments in generic functions as all the types are always part of the function signature so the compiler can always infer them. One corner case might be if using default argument values in which case support for default generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to think about is how it interacts with type inference. For example, consider these examples:

  struct X<T = Int> { }

  func f1() -> X<Double> { return X() }

  func f2() -> X<Int> { return X() }
  func f2() -> X<Double> { return X() }

  func f3<T>(_: T) -> X<T> { return X() }

  let x1: X = f1() // okay: x1 has type X<Double>?
  let x2: X = f2() // ambiguous?
  let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
  let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a particular type, fill in a default” that is used for literals. That rule could be used here… or we could do something else. This should be discussed in the proposal.

Thanks for working on this!

There's an interesting parallel to the default behavior of literals. The type of a number or string literal is inferred from type context, or falls back to a default type like Int or String if that doesn't come up with an answer. You could think of that of saying the 'Self' type of the protocol constraint has a default (and maybe that's how we'd generalize the "default type for a protocol" feature if we wanted to.) It makes sense to me to follow a similar model for generic parameter defaults; that way, there's one consistent rule that applies.

-Joe

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

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


(Trent Nadeau) #5

The proposal looks good to me with one possible concern. I'm leaning toward
types that use the defaults should still require the angle brackets, X<>.
This makes it clear that you're using a generic type. That leads me to
think that the examples Doug gave should be an error as the explicit types
on the `let`s should either be omitted completely or fully specified (as
X<>, X<Double>, X<Int>, etc.).

···

On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing
default generic arguments which I think would be nice addition to the
language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not
coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to
think about is how it interacts with type inference. For example, consider
these examples:

struct X<T = Int> { }

func f1() -> X<Double> { return X() }

func f2() -> X<Int> { return X() }
func f2() -> X<Double> { return X() }

func f3<T>(_: T) -> X<T> { return X() }

let x1: X = f1() // okay: x1 has type X<Double>?
let x2: X = f2() // ambiguous?
let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a
particular type, fill in a default” that is used for literals. That rule
could be used here… or we could do something else. This should be discussed
in the proposal.

Thanks for working on this!

There's an interesting parallel to the default behavior of literals. The
type of a number or string literal is inferred from type context, or falls
back to a default type like Int or String if that doesn't come up with an
answer. You could think of that of saying the 'Self' type of the protocol
constraint has a default (and maybe that's how we'd generalize the "default
type for a protocol" feature if we wanted to.) It makes sense to me to
follow a similar model for generic parameter defaults; that way, there's
one consistent rule that applies.

-Joe

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

--
Trent Nadeau


(Sean Heber) #6

I agree. I don’t think empty angle brackets convey anything useful to the reader.

l8r
Sean

···

On Jan 23, 2017, at 1:25 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

I am against requiring empty angle brackets. I could live with it either way, but I think that one reason to provide default types is to hide the detail that there is a type parameter until such a time as it is needed. Empty angle brackets call attention to the feature in a manner that discards any possible gains on this front. Empty angle brackets would be confusing to explain to someone new to the language and–more importantly–shouldn't be necessary to explain in the "falling back to defaults" case.

On Mon, Jan 23, 2017 at 1:41 PM, Trent Nadeau via swift-evolution <swift-evolution@swift.org> wrote:
The proposal looks good to me with one possible concern. I'm leaning toward types that use the defaults should still require the angle brackets, X<>. This makes it clear that you're using a generic type. That leads me to think that the examples Doug gave should be an error as the explicit types on the `let`s should either be omitted completely or fully specified (as X<>, X<Double>, X<Int>, etc.).

On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution <swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing default generic arguments which I think would be nice addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not coved by the proposal and I don't think that we need default generic arguments in generic functions as all the types are always part of the function signature so the compiler can always infer them. One corner case might be if using default argument values in which case support for default generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to think about is how it interacts with type inference. For example, consider these examples:

  struct X<T = Int> { }

  func f1() -> X<Double> { return X() }

  func f2() -> X<Int> { return X() }
  func f2() -> X<Double> { return X() }

  func f3<T>(_: T) -> X<T> { return X() }

  let x1: X = f1() // okay: x1 has type X<Double>?
  let x2: X = f2() // ambiguous?
  let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
  let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a particular type, fill in a default” that is used for literals. That rule could be used here… or we could do something else. This should be discussed in the proposal.

Thanks for working on this!

There's an interesting parallel to the default behavior of literals. The type of a number or string literal is inferred from type context, or falls back to a default type like Int or String if that doesn't come up with an answer. You could think of that of saying the 'Self' type of the protocol constraint has a default (and maybe that's how we'd generalize the "default type for a protocol" feature if we wanted to.) It makes sense to me to follow a similar model for generic parameter defaults; that way, there's one consistent rule that applies.

-Joe

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

--
Trent Nadeau

_______________________________________________
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


(TJ Usiyan) #7

I am against requiring empty angle brackets. I could live with it either
way, but I think that one reason to provide default types is to hide the
detail that there is a type parameter until such a time as it is needed.
Empty angle brackets call attention to the feature in a manner that
discards any possible gains on this front. Empty angle brackets would be
confusing to explain to someone new to the language and–more
importantly–shouldn't be necessary to explain in the "falling back to
defaults" case.

···

On Mon, Jan 23, 2017 at 1:41 PM, Trent Nadeau via swift-evolution < swift-evolution@swift.org> wrote:

The proposal looks good to me with one possible concern. I'm leaning
toward types that use the defaults should still require the angle brackets,
X<>. This makes it clear that you're using a generic type. That leads me to
think that the examples Doug gave should be an error as the explicit types
on the `let`s should either be omitted completely or fully specified (as
X<>, X<Double>, X<Int>, etc.).

On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution < >> swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution < >> swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591
) proposing default generic arguments which I think would be nice
addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not
coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to
think about is how it interacts with type inference. For example, consider
these examples:

struct X<T = Int> { }

func f1() -> X<Double> { return X() }

func f2() -> X<Int> { return X() }
func f2() -> X<Double> { return X() }

func f3<T>(_: T) -> X<T> { return X() }

let x1: X = f1() // okay: x1 has type X<Double>?
let x2: X = f2() // ambiguous?
let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a
particular type, fill in a default” that is used for literals. That rule
could be used here… or we could do something else. This should be discussed
in the proposal.

Thanks for working on this!

There's an interesting parallel to the default behavior of literals. The
type of a number or string literal is inferred from type context, or falls
back to a default type like Int or String if that doesn't come up with an
answer. You could think of that of saying the 'Self' type of the protocol
constraint has a default (and maybe that's how we'd generalize the "default
type for a protocol" feature if we wanted to.) It makes sense to me to
follow a similar model for generic parameter defaults; that way, there's
one consistent rule that applies.

-Joe

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

--
Trent Nadeau

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


(David Waite) #8

You do have empty angle brackets today, which indicate an inferred generic argument rather than a defaulted generic argument. See:

  1> let a = 1
  2> let b:Optional = a
b: Int? = 1

If Swift had defined Optional as Optional<Wrapped = Any>, statement 2 would be ambiguous.

I have a hard time coming up with a realistic generic which can have every argument defaulted. If someone can’t give a real-world example, I would require empty angle brackets just to differentiate defaulted vs inferred arguments.

-DW

···

a: Int = 1

On Jan 23, 2017, at 12:25 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

I am against requiring empty angle brackets. I could live with it either way, but I think that one reason to provide default types is to hide the detail that there is a type parameter until such a time as it is needed. Empty angle brackets call attention to the feature in a manner that discards any possible gains on this front. Empty angle brackets would be confusing to explain to someone new to the language and–more importantly–shouldn't be necessary to explain in the "falling back to defaults" case.

On Mon, Jan 23, 2017 at 1:41 PM, Trent Nadeau via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
The proposal looks good to me with one possible concern. I'm leaning toward types that use the defaults should still require the angle brackets, X<>. This makes it clear that you're using a generic type. That leads me to think that the examples Doug gave should be an error as the explicit types on the `let`s should either be omitted completely or fully specified (as X<>, X<Double>, X<Int>, etc.).


(Trent Nadeau) #9

The fact that it has a default generic argument means that it has a "knob"
to turn based on changes in needs of the code. For example, if you had a
struct that used a T (defaulted to Int) for a field, and that field's range
should become a Double in your use case, you know that there's something
you can change to get that behavior, while just X might look like you'd
need to create your own type.

···

On Mon, Jan 23, 2017 at 2:41 PM, Michael Ilseman <milseman@apple.com> wrote:

On Jan 23, 2017, at 10:41 AM, Trent Nadeau via swift-evolution < > swift-evolution@swift.org> wrote:

The proposal looks good to me with one possible concern. I'm leaning
toward types that use the defaults should still require the angle brackets,
X<>. This makes it clear that you're using a generic type.

What are the perceived benefits by making it explicit that you’re using a
defaulted-or-inferred generic type? What important pieces of information
would the presence of an explicit “<>” communicate to future
readers/maintainers of the code?

That leads me to think that the examples Doug gave should be an error as
the explicit types on the `let`s should either be omitted completely or
fully specified (as X<>, X<Double>, X<Int>, etc.).

On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution < >> swift-evolution@swift.org> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution < >> swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591
) proposing default generic arguments which I think would be nice
addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not
coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to
think about is how it interacts with type inference. For example, consider
these examples:

struct X<T = Int> { }

func f1() -> X<Double> { return X() }

func f2() -> X<Int> { return X() }
func f2() -> X<Double> { return X() }

func f3<T>(_: T) -> X<T> { return X() }

let x1: X = f1() // okay: x1 has type X<Double>?
let x2: X = f2() // ambiguous?
let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a
particular type, fill in a default” that is used for literals. That rule
could be used here… or we could do something else. This should be discussed
in the proposal.

Thanks for working on this!

There's an interesting parallel to the default behavior of literals. The
type of a number or string literal is inferred from type context, or falls
back to a default type like Int or String if that doesn't come up with an
answer. You could think of that of saying the 'Self' type of the protocol
constraint has a default (and maybe that's how we'd generalize the "default
type for a protocol" feature if we wanted to.) It makes sense to me to
follow a similar model for generic parameter defaults; that way, there's
one consistent rule that applies.

-Joe

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

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

--
Trent Nadeau


(TJ Usiyan) #10

A `Parser<Output = Void, StringType = String> where StringType :
ParsableStringType`

`Void` is a fine default (somewhat questionable but follow me) because
`Void?` ends up having the same info as a boolean which means that our
Parser is, effectively, a recognizer. Thinking about it,
`GrammarRecognizer<StringType = String> where StringType :
ParsableStringType` sidesteps the issue entirely and gives a second example
of when we could fill all type parameters.

···

On Mon, Jan 23, 2017 at 4:32 PM, David Waite <david@alkaline-solutions.com> wrote:

You do have empty angle brackets today, which indicate an inferred generic
argument rather than a defaulted generic argument. See:

  1> let a = 1
a: Int = 1
  2> let b:Optional = a
b: Int? = 1

If Swift had defined Optional as Optional<Wrapped = Any>, statement 2
would be ambiguous.

I have a hard time coming up with a realistic generic which can have every
argument defaulted. If someone can’t give a real-world example, I would
require empty angle brackets just to differentiate defaulted vs inferred
arguments.

-DW

On Jan 23, 2017, at 12:25 PM, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:

I am against requiring empty angle brackets. I could live with it either
way, but I think that one reason to provide default types is to hide the
detail that there is a type parameter until such a time as it is needed.
Empty angle brackets call attention to the feature in a manner that
discards any possible gains on this front. Empty angle brackets would be
confusing to explain to someone new to the language and–more
importantly–shouldn't be necessary to explain in the "falling back to
defaults" case.

On Mon, Jan 23, 2017 at 1:41 PM, Trent Nadeau via swift-evolution < > swift-evolution@swift.org> wrote:

The proposal looks good to me with one possible concern. I'm leaning
toward types that use the defaults should still require the angle brackets,
X<>. This makes it clear that you're using a generic type. That leads me to
think that the examples Doug gave should be an error as the explicit types
on the `let`s should either be omitted completely or fully specified (as
X<>, X<Double>, X<Int>, etc.).


(Matthew Johnson) #11

This proposal looks good to me. I have been looking forward to more flexible generic arguments for a while.

I agree with previous commenters who prefer the option to leave off the angle brackets when all parameters have defaults.

The proposal specifically mentions that the syntax is inspired by that of function arguments. This is good, but I wonder if maybe we should draw further inspiration from function arguments and also add parameter labels for generic arguments. Both feel like low hanging fruit in the generics area (correct me if I’m wrong about that) and it would be great to see both enhancements make it into Swift 4.

···

On Jan 23, 2017, at 9:55 AM, Srđan Rašić via swift-evolution <swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing default generic arguments which I think would be nice addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not coved by the proposal and I don't think that we need default generic arguments in generic functions as all the types are always part of the function signature so the compiler can always infer them. One corner case might be if using default argument values in which case support for default generic arguments in functions might be useful.

It would be great to hear your opinions and suggestions so I can refine the proposal.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Thomas Visser) #12

I think I like the explicitness of a required <> as well. (It reminds me of Java, where you can leave out the parameter when the type is known.)

I am however not sure if we could add this without breaking current valid Swift 3 syntax. The following three statements are correct in Swift 3:

  let one = X<Int>()
  let two: X = X<Int>()
  let three: X<Int> = X()

Only the following one is (obviously) incorrect:

  let four: X = X()

If we would require empty brackets, even if the type can be inferred, I could see example three stop working.

···

On 23 Jan 2017, at 19:41, Trent Nadeau via swift-evolution <swift-evolution@swift.org> wrote:

The proposal looks good to me with one possible concern. I'm leaning toward types that use the defaults should still require the angle brackets, X<>. This makes it clear that you're using a generic type. That leads me to think that the examples Doug gave should be an error as the explicit types on the `let`s should either be omitted completely or fully specified (as X<>, X<Double>, X<Int>, etc.).

On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing default generic arguments which I think would be nice addition to the language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not coved by the proposal and I don't think that we need default generic arguments in generic functions as all the types are always part of the function signature so the compiler can always infer them. One corner case might be if using default argument values in which case support for default generic arguments in functions might be useful.

The proposal looks fairly straightforward and reasonable. One thing to think about is how it interacts with type inference. For example, consider these examples:

  struct X<T = Int> { }

  func f1() -> X<Double> { return X() }

  func f2() -> X<Int> { return X() }
  func f2() -> X<Double> { return X() }

  func f3<T>(_: T) -> X<T> { return X() }

  let x1: X = f1() // okay: x1 has type X<Double>?
  let x2: X = f2() // ambiguous?
  let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
  let x3b: X = f3(1) // okay: x3a has type X<Int>?

The type checker already has some notion of “if you can’t infer a particular type, fill in a default” that is used for literals. That rule could be used here… or we could do something else. This should be discussed in the proposal.

Thanks for working on this!

There's an interesting parallel to the default behavior of literals. The type of a number or string literal is inferred from type context, or falls back to a default type like Int or String if that doesn't come up with an answer. You could think of that of saying the 'Self' type of the protocol constraint has a default (and maybe that's how we'd generalize the "default type for a protocol" feature if we wanted to.) It makes sense to me to follow a similar model for generic parameter defaults; that way, there's one consistent rule that applies.

-Joe

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

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


(Srđan Rašić) #13

I too agree that empty angle brackets are redundant because the information
they convey is not useful enough to justify the syntax clutter. I would
value clean syntax in this case more than explicitness and I think that
would go along with Swift's philosophy. I would compare this to Swift's
type inference where one can omit type information that's already known to
the compiler in order to increase readability.

For example, if you had a struct that used a T (defaulted to Int) for a

field, and that field's range should become a Double in your use case, you
know that there's something you can change to get that behavior, while just
X might look like you'd need to create your own type.

I think such cases would be extremely rare and one would have to be very
ignorant about the types he/she works with. Additionally, that syntax is
useful only for types with one generic argument. Say we have `Promise<T, E
= Error>` and declare property as `let p: Promise<Int>`. How would you
convey the information that there is a second argument that could be
changed? Keeping the comma would be very ugly :slight_smile:

···

On Mon, Jan 23, 2017 at 8:51 PM, Sean Heber <sean@fifthace.com> wrote:

I agree. I don’t think empty angle brackets convey anything useful to the
reader.

l8r
Sean

> On Jan 23, 2017, at 1:25 PM, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:
>
> I am against requiring empty angle brackets. I could live with it either
way, but I think that one reason to provide default types is to hide the
detail that there is a type parameter until such a time as it is needed.
Empty angle brackets call attention to the feature in a manner that
discards any possible gains on this front. Empty angle brackets would be
confusing to explain to someone new to the language and–more
importantly–shouldn't be necessary to explain in the "falling back to
defaults" case.
>
> On Mon, Jan 23, 2017 at 1:41 PM, Trent Nadeau via swift-evolution < > swift-evolution@swift.org> wrote:
> The proposal looks good to me with one possible concern. I'm leaning
toward types that use the defaults should still require the angle brackets,
X<>. This makes it clear that you're using a generic type. That leads me to
think that the examples Doug gave should be an error as the explicit types
on the `let`s should either be omitted completely or fully specified (as
X<>, X<Double>, X<Int>, etc.).
>
> On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org> wrote:
>
>> On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>>
>>> On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution < > swift-evolution@swift.org> wrote:
>>>
>>> Hi Everyone,
>>>
>>> I've opened a PR (https://github.com/apple/swift-evolution/pull/591)
proposing default generic arguments which I think would be nice addition to
the language. They are also mentioned in "Generic manifesto".
>>>
>>> The proposal is focusing around generic types. Generic functions are
not coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.
>>
>> The proposal looks fairly straightforward and reasonable. One thing to
think about is how it interacts with type inference. For example, consider
these examples:
>>
>> struct X<T = Int> { }
>>
>> func f1() -> X<Double> { return X() }
>>
>> func f2() -> X<Int> { return X() }
>> func f2() -> X<Double> { return X() }
>>
>> func f3<T>(_: T) -> X<T> { return X() }
>>
>> let x1: X = f1() // okay: x1 has type X<Double>?
>> let x2: X = f2() // ambiguous?
>> let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
>> let x3b: X = f3(1) // okay: x3a has type X<Int>?
>>
>> The type checker already has some notion of “if you can’t infer a
particular type, fill in a default” that is used for literals. That rule
could be used here… or we could do something else. This should be discussed
in the proposal.
>>
>> Thanks for working on this!
>
> There's an interesting parallel to the default behavior of literals. The
type of a number or string literal is inferred from type context, or falls
back to a default type like Int or String if that doesn't come up with an
answer. You could think of that of saying the 'Self' type of the protocol
constraint has a default (and maybe that's how we'd generalize the "default
type for a protocol" feature if we wanted to.) It makes sense to me to
follow a similar model for generic parameter defaults; that way, there's
one consistent rule that applies.
>
> -Joe
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
> --
> Trent Nadeau
>
> _______________________________________________
> 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


(Anton Zhilin) #14

Current alternative to default generic arguments is typealias, like
basic_string and string in C++:

struct BasicBigInt<T> { ... }
typealias BigInt = BasicBigInt<Int64>

It’s not *that* ugly. It keeps type inference rules simpler, easier to
understand and more explicit.
As someone noted, current type inference for generic initializers works
satisfactory in 99% cases. Is it really worth it to try to cover 99.9% at
the cost of complexity of type inference rules?

On the other hand, in the wild, there may exist types with 3 or even more
generic parameters that have sensible default values. [Research needed]
For such complex cases, I think, it makes sense to add default generic
parameters *only* together with generic parameter labels.
Also, in such cases, functions on types could often help. And that story is
even out of scope of Swift 4 Phase 2.


(Alexis) #15

To elaborate on this, default arguments are also a powerful tool for introducing new generic parameters in a way that’s source compatible. (potentially ABI compatible? Haven’t thought out implications of that). For instance, if you have a collection type, and decide to expose the allocator as a type parameter, defaults give you a backwards compatible way to do that. Making developers annotate “I’m using defaults” throws that away. If you make this “only” a warning then you’re just making busywork for the 99% of developers who always wanted the default behaviour, and couldn’t care less that it’s now configurable.

This would also go against the massive precedent set by default function arguments, which never need to be acknowledged.

···

On Jan 23, 2017, at 3:18 PM, Srđan Rašić via swift-evolution <swift-evolution@swift.org> wrote:

I think such cases would be extremely rare and one would have to be very ignorant about the types he/she works with. Additionally, that syntax is useful only for types with one generic argument. Say we have `Promise<T, E = Error>` and declare property as `let p: Promise<Int>`. How would you convey the information that there is a second argument that could be changed? Keeping the comma would be very ugly :slight_smile:


(Srđan Rašić) #16

@doug:

struct X<T = Int> { }

func f1() -> X<Double> { return X() }

func f2() -> X<Int> { return X() }
func f2() -> X<Double> { return X() }

func f3<T>(_: T) -> X<T> { return X() }

let x1: X = f1() // okay: x1 has type X<Double>?

Agreed. When type is explicitly defined in the context, that type should
override the default type. In this example type is explicitly defined by
the return argument so we infer that type instead of using the default one.

let x2: X = f2() // ambiguous?

Uncertain. I might even lean to `X<Int>` because defining a property as
`let x1: X` would be considered the same as defining it as `let x1:
X<Int>`. In that case I would expect that the correct overload is inferable.

let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
let x3b: X = f3(1) // okay: x3a has type X<Int>?

Agreed. These two are related to x1. Type is defined in the context (by
inferring it from the literal) so it overrides the default argument.

I like the parallel with the default behaviour of literals - “if you can’t
infer a particular type, fill in a default". We should aim for that.

···

On Mon, Jan 23, 2017 at 9:18 PM, Srđan Rašić <srdan.rasic@gmail.com> wrote:

I too agree that empty angle brackets are redundant because the
information they convey is not useful enough to justify the syntax clutter.
I would value clean syntax in this case more than explicitness and I think
that would go along with Swift's philosophy. I would compare this to
Swift's type inference where one can omit type information that's already
known to the compiler in order to increase readability.

> For example, if you had a struct that used a T (defaulted to Int) for a
field, and that field's range should become a Double in your use case, you
know that there's something you can change to get that behavior, while just
X might look like you'd need to create your own type.

I think such cases would be extremely rare and one would have to be very
ignorant about the types he/she works with. Additionally, that syntax is
useful only for types with one generic argument. Say we have `Promise<T, E
= Error>` and declare property as `let p: Promise<Int>`. How would you
convey the information that there is a second argument that could be
changed? Keeping the comma would be very ugly :slight_smile:

On Mon, Jan 23, 2017 at 8:51 PM, Sean Heber <sean@fifthace.com> wrote:

I agree. I don’t think empty angle brackets convey anything useful to the
reader.

l8r
Sean

> On Jan 23, 2017, at 1:25 PM, T.J. Usiyan via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> I am against requiring empty angle brackets. I could live with it
either way, but I think that one reason to provide default types is to hide
the detail that there is a type parameter until such a time as it is
needed. Empty angle brackets call attention to the feature in a manner
that discards any possible gains on this front. Empty angle brackets would
be confusing to explain to someone new to the language and–more
importantly–shouldn't be necessary to explain in the "falling back to
defaults" case.
>
> On Mon, Jan 23, 2017 at 1:41 PM, Trent Nadeau via swift-evolution < >> swift-evolution@swift.org> wrote:
> The proposal looks good to me with one possible concern. I'm leaning
toward types that use the defaults should still require the angle brackets,
X<>. This makes it clear that you're using a generic type. That leads me to
think that the examples Doug gave should be an error as the explicit types
on the `let`s should either be omitted completely or fully specified (as
X<>, X<Double>, X<Int>, etc.).
>
> On Mon, Jan 23, 2017 at 1:21 PM, Joe Groff via swift-evolution < >> swift-evolution@swift.org> wrote:
>
>> On Jan 23, 2017, at 9:51 AM, Douglas Gregor via swift-evolution < >> swift-evolution@swift.org> wrote:
>>
>>
>>> On Jan 23, 2017, at 7:55 AM, Srđan Rašić via swift-evolution < >> swift-evolution@swift.org> wrote:
>>>
>>> Hi Everyone,
>>>
>>> I've opened a PR (https://github.com/apple/swift-evolution/pull/591)
proposing default generic arguments which I think would be nice addition to
the language. They are also mentioned in "Generic manifesto".
>>>
>>> The proposal is focusing around generic types. Generic functions are
not coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.
>>
>> The proposal looks fairly straightforward and reasonable. One thing to
think about is how it interacts with type inference. For example, consider
these examples:
>>
>> struct X<T = Int> { }
>>
>> func f1() -> X<Double> { return X() }
>>
>> func f2() -> X<Int> { return X() }
>> func f2() -> X<Double> { return X() }
>>
>> func f3<T>(_: T) -> X<T> { return X() }
>>
>> let x1: X = f1() // okay: x1 has type X<Double>?
>> let x2: X = f2() // ambiguous?
>> let x3a: X = f3(1.5) // okay: x3a has type X<Double>?
>> let x3b: X = f3(1) // okay: x3a has type X<Int>?
>>
>> The type checker already has some notion of “if you can’t infer a
particular type, fill in a default” that is used for literals. That rule
could be used here… or we could do something else. This should be discussed
in the proposal.
>>
>> Thanks for working on this!
>
> There's an interesting parallel to the default behavior of literals.
The type of a number or string literal is inferred from type context, or
falls back to a default type like Int or String if that doesn't come up
with an answer. You could think of that of saying the 'Self' type of the
protocol constraint has a default (and maybe that's how we'd generalize the
"default type for a protocol" feature if we wanted to.) It makes sense to
me to follow a similar model for generic parameter defaults; that way,
there's one consistent rule that applies.
>
> -Joe
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
> --
> Trent Nadeau
>
> _______________________________________________
> 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


(Xiaodi Wu) #17

While it looks nicer without the angle brackets, that suggestion is
unresponsive to David's point that we need some way to distinguish
defaulted generic arguments from inferred generic arguments.

Consider:

let a: Optional = 1 // Optional<Int>

enum FloatPreferringOptional<T = Float> {
  case some(T)
  case none
}

let b: FloatPreferringOptional = 1
// Does this give you an FloatPreferringOptional<Int>?

If the answer to the above question is "yes, T is inferred as Int" then we
need some way to express "give me the default for T, which is Float." If
the answer to the above question is "no" then we need some way to express
"don't give me the default; rather, infer type T from the right hand side."

···

On Mon, Jan 23, 2017 at 6:30 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

This proposal looks good to me. I have been looking forward to more
flexible generic arguments for a while.

I agree with previous commenters who prefer the option to leave off the
angle brackets when all parameters have defaults.

The proposal specifically mentions that the syntax is inspired by that of
function arguments. This is good, but I wonder if maybe we should draw
further inspiration from function arguments and also add parameter labels
for generic arguments. Both feel like low hanging fruit in the generics
area (correct me if I’m wrong about that) and it would be great to see both
enhancements make it into Swift 4.

On Jan 23, 2017, at 9:55 AM, Srđan Rašić via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing
default generic arguments which I think would be nice addition to the
language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not
coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.

It would be great to hear your opinions and suggestions so I can refine
the proposal.
_______________________________________________
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


(David Sweeris) #18

If we could overload the type's name, I think that’d solve my use-case pretty much completely:
protocol Endian {}
class BigEndian : Endian {}
class LittleEndian : Endian {}
class NativeEndian : Endian {}
struct BigInt<Chunk _: Integer, Endian _: Endian> {...} //Made-up syntax for indicating the generic parameters’ labels should be the same both internally and externally
typealias BigInt<T> = BigInt<Chunk: T, Endian: NativeEndian> where T: Integer
typealias BigInt = BigInt<Int>

- Dave Sweeris

···

On Jan 27, 2017, at 1:43 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

Current alternative to default generic arguments is typealias, like basic_string and string in C++:

struct BasicBigInt<T> { ... }
typealias BigInt = BasicBigInt<Int64>
It’s not that ugly. It keeps type inference rules simpler, easier to understand and more explicit.
As someone noted, current type inference for generic initializers works satisfactory in 99% cases. Is it really worth it to try to cover 99.9% at the cost of complexity of type inference rules?

On the other hand, in the wild, there may exist types with 3 or even more generic parameters that have sensible default values. [Research needed]
For such complex cases, I think, it makes sense to add default generic parameters only together with generic parameter labels.
Also, in such cases, functions on types could often help. And that story is even out of scope of Swift 4 Phase 2.


(Alexis) #19

This is a really great point, but it should be noted that this is only sufficient to accomplish source-stability. Once the standard library starts providing ABI stability, this solution won’t work for it — the type of BigInt will become BasicBigInt<Int64> which will change mangling and other things.

First-class generic defaults, on the other hand, have the potential to be built out so that any binary compiled against the old type definition continues to work. The details of what this looks like depends on precisely how the final ABI shakes out.

···

On Jan 27, 2017, at 4:43 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

Current alternative to default generic arguments is typealias, like basic_string and string in C++:

struct BasicBigInt<T> { ... }
typealias BigInt = BasicBigInt<Int64>


(Srđan Rašić) #20

If the answer to the above question is "yes, T is inferred as Int" then

we need some way to express "give me the default for T, which is Float."

I don't think that we need that. It would introduce a new level of
explicitness, "I want the default, but I don't care what the default is",
that is not really useful. If you don't care what the default type is, you
probably also don't care that you are defaulting. If you do care what the
default type is, you would explicitly sepecify it as `X<Float>`.

If the answer to the above question is "no" then we need some way to

express "don't give me the default; rather, infer type T from the right
hand side."

That would be preferred behavior. Infer from the context if possible, use
default otherwise.

···

tir. 24. jan. 2017 kl. 05.11 skrev Xiaodi Wu <xiaodi.wu@gmail.com>:

While it looks nicer without the angle brackets, that suggestion is
unresponsive to David's point that we need some way to distinguish
defaulted generic arguments from inferred generic arguments.

Consider:

let a: Optional = 1 // Optional<Int>

enum FloatPreferringOptional<T = Float> {
  case some(T)
  case none
}

let b: FloatPreferringOptional = 1
// Does this give you an FloatPreferringOptional<Int>?

If the answer to the above question is "yes, T is inferred as Int" then we
need some way to express "give me the default for T, which is Float." If
the answer to the above question is "no" then we need some way to express
"don't give me the default; rather, infer type T from the right hand side."

On Mon, Jan 23, 2017 at 6:30 PM, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

This proposal looks good to me. I have been looking forward to more
flexible generic arguments for a while.

I agree with previous commenters who prefer the option to leave off the
angle brackets when all parameters have defaults.

The proposal specifically mentions that the syntax is inspired by that of
function arguments. This is good, but I wonder if maybe we should draw
further inspiration from function arguments and also add parameter labels
for generic arguments. Both feel like low hanging fruit in the generics
area (correct me if I’m wrong about that) and it would be great to see both
enhancements make it into Swift 4.

On Jan 23, 2017, at 9:55 AM, Srđan Rašić via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Everyone,

I've opened a PR (https://github.com/apple/swift-evolution/pull/591) proposing
default generic arguments which I think would be nice addition to the
language. They are also mentioned in "Generic manifesto".

The proposal is focusing around generic types. Generic functions are not
coved by the proposal and I don't think that we need default generic
arguments in generic functions as all the types are always part of the
function signature so the compiler can always infer them. One corner case
might be if using default argument values in which case support for default
generic arguments in functions might be useful.

It would be great to hear your opinions and suggestions so I can refine
the proposal.

_______________________________________________
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