[Pitch] Simpler interpretation of a reference to a generic type with no arguments


(Slava Pestov) #1

Simpler interpretation of a reference to a generic type with no arguments

Proposal: SE-9999 <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal <http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives considered

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.


(Matthew Johnson) #2

+1 again. Thanks for ferreting out these surprising behaviors Slava!

I did not realize the shorthand you propose to remove was possible (is this documented anywhere?). On the other hand, I make use of the inference that does not work correctly in the “broken” example all the time.

This is another one that would have been pretty confusing to me had I run into it. I don’t believe there is any other context where explicit type arguments are required in a type context where they should be inferred. Fixing this is really important.

I think a reasonably strong argument can be made to remove the first behavior even without this conflict. I really like Swift’s approach of making type information explicit in signatures. Allowing it to be inferred in an obscure corner case violates that principle.

I also think it is reasonable to require the programmer to state all type information explicitly when it isn’t inferred. This makes the code more clear to a reader. Type information is either stated or not stated, never partially stated (requiring the programmer to remember what pieces are inferred as well as when partial inference is possible and when it is not possible).

It’s also worth noting that in one of your clone example the whole type could be inferred:

let cloned = clone()

···

On Jun 23, 2016, at 3:14 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no arguments

Proposal: SE-9999 <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal <http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives considered

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #3

When you mention the difficulty of an alternative, is that to say that it's
not feasible for the GenericBox in the last example to be resolved as
GenericBox<T>? From an end-user point of view, that seems to be the most
sensible behavior.

···

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution < swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no arguments

   - Proposal: SE-9999
   <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
   - Author: Slava Pestov <https://github.com/slavapestov>
   - Status: Awaiting review
   - Review manager: TBD

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>
Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>
Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules here
are the following:

   -

   If the scope from which the reference is made is nested inside the
   definition of the type or an extension thereof, omitting generic arguments
   just means to implicitly apply the arguments from context.

   For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}
extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}

   -

   If the type is referenced from an unrelated scope, we attempt to infer
   the generic parameters.

   For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}

The problem appears when the user expects the second behavior, but instead
encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be specified
explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed
design

This really just involves removing an existing piece of logic from the
type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact
on existing code

This will have a small impact on existing code that uses a pattern similar
to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives
considered
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status
quo

We could keep the current behavior, but one can argue it is not very
useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More
complex inference of generic parameters
We could attempt to unify the two rules for resolving a reference to a
generic type with no arguments, however this presents theoretical
difficulties with our constraint solver design. Even if it were easy to
implement, it would increase type checking type by creating new
possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Anton Zhilin) #4

Definitely +1. This is an "accept or reject" kind of proposal with no room
for discussion, so go ahead and submit it!


(Douglas Gregor) #5

This sounds good to me. I added the first rule a very long time ago (based on C++'s injected-class-name rules) because I felt it eliminated boilerplate, but it's caused too much confusion to be worth it. Thanks for working on this!

···

Sent from my iPhone

On Jun 23, 2016, at 1:14 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no arguments
Proposal: SE-9999
Author: Slava Pestov
Status: Awaiting review
Review manager: TBD
Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

Alternatives considered

Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #6

SGTM. I've always found this shorthand to be somewhat surprising,
including in C++ where (IIUC) it originated.

···

on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no
arguments

Proposal: SE-9999
<https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules
here are the following:

If the scope from which the reference is made is nested inside the
definition of the type or an extension thereof, omitting generic
arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer
the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be
specified explicitly with the usual Type<Args...> syntax.

--
Dave


(Slava Pestov) #7

When you mention the difficulty of an alternative, is that to say that it's not feasible for the GenericBox in the last example to be resolved as GenericBox<T>? From an end-user point of view, that seems to be the most sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in the last example. Right now it fails to type check.

Here is an example that works right now, but would not work with my proposed change:

struct GenericBox<Contents> {
  // Currently Swift resolves this as ‘GenericBox<Contents>’
  // With the new rule, we cannot infer the parameter, because there’s no expression to infer it from
  func combine(other: GenericBox) {
    …
  }
}

Basically the meaning of ‘GenericBox’ right now depends on whether it appears inside its own definition or extension thereof, or not. The behavior when it appears elsewhere is more general — we infer the parameters from the surrounding expression, instead of assuming they’re equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it well.

Slava

···

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Simpler interpretation of a reference to a generic type with no arguments

Proposal: SE-9999 <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal <http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives considered

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Slava Pestov) #8

When you mention the difficulty of an alternative, is that to say that it's not feasible for the GenericBox in the last example to be resolved as GenericBox<T>? From an end-user point of view, that seems to be the most sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in the last example. Right now it fails to type check.

This should make it clearer:

struct GenericBox<Contents> {
  let contents: Contents

  func transform<Result>(f: (Contents) -> Result) -> GenericBox<Result> {
    // If you change this to just ‘GenericBox(contents: …)’, it does not type check
    return GenericBox<Result>(contents: f(contents))
  }
}

func transform<Contents, Result>(box: GenericBox<Contents>,
                  f: (Contents) -> Result) -> GenericBox<Result> {
  // But this is totally fine!
  return GenericBox(contents: f(box.contents))
}

I suspect most people do not expect the first case to fail, and it is not immediately obvious why it fails when the second example type checks.

···

On Jun 23, 2016, at 1:30 PM, Slava Pestov <spestov@apple.com> wrote:

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Here is an example that works right now, but would not work with my proposed change:

struct GenericBox<Contents> {
  // Currently Swift resolves this as ‘GenericBox<Contents>’
  // With the new rule, we cannot infer the parameter, because there’s no expression to infer it from
  func combine(other: GenericBox) {
    …
  }
}

Basically the meaning of ‘GenericBox’ right now depends on whether it appears inside its own definition or extension thereof, or not. The behavior when it appears elsewhere is more general — we infer the parameters from the surrounding expression, instead of assuming they’re equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it well.

Slava

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Simpler interpretation of a reference to a generic type with no arguments

Proposal: SE-9999 <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal <http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives considered

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Slava Pestov) #9

So the only real downside of this is that if you don’t refer/use all of the generic types in this sort of situation, then it couldn’t figure it out? So the following wouldn’t be a problem even if rule 1 was eliminated because this actually ties other.value to self’s value and implies that other must be GenericBox<Contents>?

struct GenericBox<Contents: AnyObject> {
   let value: Contents

   func test(other: GenericBox) -> Bool {
       return value === other.value
   }
}

I might be totally not understanding, though. :stuck_out_tongue:

This would *not* work with my proposed change. Even though we could theoretically infer that the parameter must be a GenericBox<Contents> to make the === type check, in practice Swift’s type checker operates at the expression level.

Global type inference is not something we’re considering, because of the compile-time cost, increased difficulty in producing localized diagnostics, and the loss of too much explicit type information.

···

On Jun 23, 2016, at 1:48 PM, Sean Heber <sean@fifthace.com> wrote:

l8r
Sean

On Jun 23, 2016, at 3:30 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

When you mention the difficulty of an alternative, is that to say that it's not feasible for the GenericBox in the last example to be resolved as GenericBox<T>? From an end-user point of view, that seems to be the most sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in the last example. Right now it fails to type check.

Here is an example that works right now, but would not work with my proposed change:

struct GenericBox<Contents> {
  // Currently Swift resolves this as ‘GenericBox<Contents>’
  // With the new rule, we cannot infer the parameter, because there’s no expression to infer it from
  func combine(other: GenericBox) {
    …
  }
}

Basically the meaning of ‘GenericBox’ right now depends on whether it appears inside its own definition or extension thereof, or not. The behavior when it appears elsewhere is more general — we infer the parameters from the surrounding expression, instead of assuming they’re equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it well.

Slava

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:
Simpler interpretation of a reference to a generic type with no arguments

  • Proposal: SE-9999
  • Author: Slava Pestov
  • Status: Awaiting review
  • Review manager: TBD
Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

  • If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents>
{

let
contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>

func clone() ->
GenericBox {

return
GenericBox(contents: contents)
}
}

extension
GenericBox {

func print
() {

// Equivalent to: let cloned: GenericBox<Contents>

let cloned: GenericBox =
clone()

print(cloned.
contents)
}
}

  • If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int

{

// Equivalent to: GenericBox<Int>(contents: 123)

return GenericBox(contents: 123
)
}

The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension
GenericBox {

func transform<T>(f: Contents -> T) ->
GenericBox<T> {

// We resolve 'GenericBox' as 'GenericBox<Contents>', rather than

// inferring the type parameter

return
GenericBox(contents: f(contents))
}
}

Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

Alternatives considered

Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
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


(Sean Heber) #10

So the only real downside of this is that if you don’t refer/use all of the generic types in this sort of situation, then it couldn’t figure it out? So the following wouldn’t be a problem even if rule 1 was eliminated because this actually ties other.value to self’s value and implies that other must be GenericBox<Contents>?

struct GenericBox<Contents: AnyObject> {
    let value: Contents
    
    func test(other: GenericBox) -> Bool {
        return value === other.value
    }
}

I might be totally not understanding, though. :stuck_out_tongue:

l8r
Sean

···

On Jun 23, 2016, at 3:30 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

When you mention the difficulty of an alternative, is that to say that it's not feasible for the GenericBox in the last example to be resolved as GenericBox<T>? From an end-user point of view, that seems to be the most sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in the last example. Right now it fails to type check.

Here is an example that works right now, but would not work with my proposed change:

struct GenericBox<Contents> {
  // Currently Swift resolves this as ‘GenericBox<Contents>’
  // With the new rule, we cannot infer the parameter, because there’s no expression to infer it from
  func combine(other: GenericBox) {
    …
  }
}

Basically the meaning of ‘GenericBox’ right now depends on whether it appears inside its own definition or extension thereof, or not. The behavior when it appears elsewhere is more general — we infer the parameters from the surrounding expression, instead of assuming they’re equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it well.

Slava

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:
Simpler interpretation of a reference to a generic type with no arguments

  • Proposal: SE-9999
  • Author: Slava Pestov
  • Status: Awaiting review
  • Review manager: TBD
Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

  • If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents>
{
  
let
contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>

func clone() ->
GenericBox {
    
return
GenericBox(contents: contents)
  }
}

extension
GenericBox {
  
func print
() {
    
// Equivalent to: let cloned: GenericBox<Contents>

let cloned: GenericBox =
clone()
    
print(cloned.
contents)
  }
}

  • If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int
> {
  
// Equivalent to: GenericBox<Int>(contents: 123)

return GenericBox(contents: 123
)
}

The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension
GenericBox {

func transform<T>(f: Contents -> T) ->
GenericBox<T> {
    
// We resolve 'GenericBox' as 'GenericBox<Contents>', rather than

// inferring the type parameter

return
GenericBox(contents: f(contents))
  }
}

Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

Alternatives considered

Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
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) #11

Sorry, it's I who is saying things all wrong. I meant to ask, is it
feasible to keep both behaviors but have #2 "win" over #1, instead of
getting rid of behavior #1 entirely?

···

On Thu, Jun 23, 2016 at 15:30 Slava Pestov <spestov@apple.com> wrote:

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

When you mention the difficulty of an alternative, is that to say that
it's not feasible for the GenericBox in the last example to be resolved as
GenericBox<T>? From an end-user point of view, that seems to be the most
sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in
the last example. Right now it fails to type check.

Here is an example that works right now, but would not work with my
proposed change:

struct GenericBox<Contents> {
// Currently Swift resolves this as ‘GenericBox<Contents>’
// With the new rule, we cannot infer the parameter, because there’s no
expression to infer it from
func combine(other: GenericBox) {

}
}

Basically the meaning of ‘GenericBox’ right now depends on whether it
appears inside its own definition or extension thereof, or not. The
behavior when it appears elsewhere is more general — we infer the
parameters from the surrounding expression, instead of assuming they’re
equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it
well.

Slava

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution < > swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no arguments

   - Proposal: SE-9999
   <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
   - Author: Slava Pestov <https://github.com/slavapestov>
   - Status: Awaiting review
   - Review manager: TBD

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>
Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>
Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules here
are the following:

   -

   If the scope from which the reference is made is nested inside the
   definition of the type or an extension thereof, omitting generic arguments
   just means to implicitly apply the arguments from context.

   For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}
extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}

   -

   If the type is referenced from an unrelated scope, we attempt to
   infer the generic parameters.

   For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}

The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be specified
explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed
design

This really just involves removing an existing piece of logic from the
type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact
on existing code

This will have a small impact on existing code that uses a pattern
similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives
considered
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status
quo

We could keep the current behavior, but one can argue it is not very
useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More
complex inference of generic parameters
We could attempt to unify the two rules for resolving a reference to a
generic type with no arguments, however this presents theoretical
difficulties with our constraint solver design. Even if it were easy to
implement, it would increase type checking type by creating new
possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Pyry Jahkola) #12

I was reminded of this proposal which seems like an obvious win in clarity. Still planning to submit it, Slava?

— Pyry

···

On 28 Jun 2016, at 21:13, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no
arguments

Proposal: SE-9999
<https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules
here are the following:

If the scope from which the reference is made is nested inside the
definition of the type or an extension thereof, omitting generic
arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
let contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>
func clone() -> GenericBox {
   return GenericBox(contents: contents)
}
}

extension GenericBox {
func print() {
   // Equivalent to: let cloned: GenericBox<Contents>
   let cloned: GenericBox = clone()
   print(cloned.contents)
}
}
If the type is referenced from an unrelated scope, we attempt to infer
the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
// Equivalent to: GenericBox<Int>(contents: 123)
return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

func transform<T>(f: Contents -> T) -> GenericBox<T> {
   // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
   // inferring the type parameter
   return GenericBox(contents: f(contents))
}
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be
specified explicitly with the usual Type<Args...> syntax.

SGTM. I've always found this shorthand to be somewhat surprising,
including in C++ where (IIUC) it originated.

--
Dave


(Slava Pestov) #13

Sorry, it's I who is saying things all wrong. I meant to ask, is it feasible to keep both behaviors but have #2 "win" over #1, instead of getting rid of behavior #1 entirely?

I suspect there might be some way, but I think it would have to be some kind of one-off hack, which is not in line with our long-term goal of making the type checker more maintainable and correct ‘by construction’.

···

On Jun 23, 2016, at 1:34 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Thu, Jun 23, 2016 at 15:30 Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

When you mention the difficulty of an alternative, is that to say that it's not feasible for the GenericBox in the last example to be resolved as GenericBox<T>? From an end-user point of view, that seems to be the most sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in the last example. Right now it fails to type check.

Here is an example that works right now, but would not work with my proposed change:

struct GenericBox<Contents> {
  // Currently Swift resolves this as ‘GenericBox<Contents>’
  // With the new rule, we cannot infer the parameter, because there’s no expression to infer it from
  func combine(other: GenericBox) {
    …
  }
}

Basically the meaning of ‘GenericBox’ right now depends on whether it appears inside its own definition or extension thereof, or not. The behavior when it appears elsewhere is more general — we infer the parameters from the surrounding expression, instead of assuming they’re equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it well.

Slava

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Simpler interpretation of a reference to a generic type with no arguments

Proposal: SE-9999 <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal <http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic arguments applied in a handful of special cases. The two primary rules here are the following:

If the scope from which the reference is made is nested inside the definition of the type or an extension thereof, omitting generic arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}

extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}
If the type is referenced from an unrelated scope, we attempt to infer the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but instead encounters the first. For example, the following does not type check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed solution

The proposed solution is to remove the first rule altogether. If the generic parameters cannot be inferred from context, they must be specified explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed design

This really just involves removing an existing piece of logic from the type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact on existing code

This will have a small impact on existing code that uses a pattern similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives considered

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status quo

We could keep the current behavior, but one can argue it is not very useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More complex inference of generic parameters

We could attempt to unify the two rules for resolving a reference to a generic type with no arguments, however this presents theoretical difficulties with our constraint solver design. Even if it were easy to implement, it would increase type checking type by creating new possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #14

Good to know. I absolutely agree that the gains to be had here wouldn't be
worth a one-off hack.

···

On Thu, Jun 23, 2016 at 15:36 Slava Pestov <spestov@apple.com> wrote:

On Jun 23, 2016, at 1:34 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Sorry, it's I who is saying things all wrong. I meant to ask, is it
feasible to keep both behaviors but have #2 "win" over #1, instead of
getting rid of behavior #1 entirely?

I suspect there might be some way, but I think it would have to be some
kind of one-off hack, which is not in line with our long-term goal of
making the type checker more maintainable and correct ‘by construction’.

On Thu, Jun 23, 2016 at 15:30 Slava Pestov <spestov@apple.com> wrote:

On Jun 23, 2016, at 1:27 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

When you mention the difficulty of an alternative, is that to say that
it's not feasible for the GenericBox in the last example to be resolved as
GenericBox<T>? From an end-user point of view, that seems to be the most
sensible behavior.

With my proposed change, GenericBox would be resolved as GenericBox<T> in
the last example. Right now it fails to type check.

Here is an example that works right now, but would not work with my
proposed change:

struct GenericBox<Contents> {
// Currently Swift resolves this as ‘GenericBox<Contents>’
// With the new rule, we cannot infer the parameter, because there’s no
expression to infer it from
func combine(other: GenericBox) {

}
}

Basically the meaning of ‘GenericBox’ right now depends on whether it
appears inside its own definition or extension thereof, or not. The
behavior when it appears elsewhere is more general — we infer the
parameters from the surrounding expression, instead of assuming they’re
equal to the context parameters.

This is a subtle change — definitely let me know if I’m not explaining it
well.

Slava

On Thu, Jun 23, 2016 at 15:14 Slava Pestov via swift-evolution < >> swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no arguments

   - Proposal: SE-9999
   <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
   - Author: Slava Pestov <https://github.com/slavapestov>
   - Status: Awaiting review
   - Review manager: TBD

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>
Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>
Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules here
are the following:

   -

   If the scope from which the reference is made is nested inside the
   definition of the type or an extension thereof, omitting generic arguments
   just means to implicitly apply the arguments from context.

   For example,

struct GenericBox<Contents> {
  let contents: Contents

  // Equivalent to: func clone() -> GenericBox<Contents>
  func clone() -> GenericBox {
    return GenericBox(contents: contents)
  }
}
extension GenericBox {
  func print() {
    // Equivalent to: let cloned: GenericBox<Contents>
    let cloned: GenericBox = clone()
    print(cloned.contents)
  }
}

   -

   If the type is referenced from an unrelated scope, we attempt to
   infer the generic parameters.

   For example,

func makeABox() -> GenericBox<Int> {
  // Equivalent to: GenericBox<Int>(contents: 123)
  return GenericBox(contents: 123)
}

The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

  func transform<T>(f: Contents -> T) -> GenericBox<T> {
    // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
    // inferring the type parameter
    return GenericBox(contents: f(contents))
  }
}

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be specified
explicitly with the usual Type<Args...> syntax.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#detailed-design>Detailed
design

This really just involves removing an existing piece of logic from the
type resolver code.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#impact-on-existing-code>Impact
on existing code

This will have a small impact on existing code that uses a pattern
similar to the above.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#alternatives-considered>Alternatives
considered
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#status-quo>Status
quo

We could keep the current behavior, but one can argue it is not very
useful, and adds a special case where one is not needed.

<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#more-complex-inference-of-generic-parameters>More
complex inference of generic parameters
We could attempt to unify the two rules for resolving a reference to a
generic type with no arguments, however this presents theoretical
difficulties with our constraint solver design. Even if it were easy to
implement, it would increase type checking type by creating new
possibilities to consider, with very little actual benefit.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Slava Pestov) #15

I could if there’s interest. Since we intend on maintaining source compatibility, it will not result in a simpler implementation, though, since we’ll need to keep the old code path around for Swift 3 mode. Still worth it?

Slava

···

On Oct 11, 2016, at 1:58 PM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

I was reminded of this proposal which seems like an obvious win in clarity. Still planning to submit it, Slava?

— Pyry

On 28 Jun 2016, at 21:13, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no
arguments

Proposal: SE-9999
<https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules
here are the following:

If the scope from which the reference is made is nested inside the
definition of the type or an extension thereof, omitting generic
arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
let contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>
func clone() -> GenericBox {
  return GenericBox(contents: contents)
}
}

extension GenericBox {
func print() {
  // Equivalent to: let cloned: GenericBox<Contents>
  let cloned: GenericBox = clone()
  print(cloned.contents)
}
}
If the type is referenced from an unrelated scope, we attempt to infer
the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
// Equivalent to: GenericBox<Int>(contents: 123)
return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

func transform<T>(f: Contents -> T) -> GenericBox<T> {
  // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
  // inferring the type parameter
  return GenericBox(contents: f(contents))
}
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be
specified explicitly with the usual Type<Args...> syntax.

SGTM. I've always found this shorthand to be somewhat surprising,
including in C++ where (IIUC) it originated.

--
Dave


(Robert Widmann) #16

+1. I don't use this feature at all and (by extension) don't think there are many situations where it's useful.

~Robert Widmann

2016/10/11 18:03、Slava Pestov via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

I could if there’s interest. Since we intend on maintaining source compatibility, it will not result in a simpler implementation, though, since we’ll need to keep the old code path around for Swift 3 mode. Still worth it?

Slava

On Oct 11, 2016, at 1:58 PM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

I was reminded of this proposal which seems like an obvious win in clarity. Still planning to submit it, Slava?

— Pyry

On 28 Jun 2016, at 21:13, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no
arguments

Proposal: SE-9999
<https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules
here are the following:

If the scope from which the reference is made is nested inside the
definition of the type or an extension thereof, omitting generic
arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
let contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>
func clone() -> GenericBox {
return GenericBox(contents: contents)
}
}

extension GenericBox {
func print() {
// Equivalent to: let cloned: GenericBox<Contents>
let cloned: GenericBox = clone()
print(cloned.contents)
}
}
If the type is referenced from an unrelated scope, we attempt to infer
the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
// Equivalent to: GenericBox<Int>(contents: 123)
return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

func transform<T>(f: Contents -> T) -> GenericBox<T> {
// We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
// inferring the type parameter
return GenericBox(contents: f(contents))
}
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be
specified explicitly with the usual Type<Args...> syntax.

SGTM. I've always found this shorthand to be somewhat surprising,
including in C++ where (IIUC) it originated.

--
Dave

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


(TJ Usiyan) #17

Painful +1.

I use the first one a whole lot in a project and it is going to get ugly.
That said… I can see how it is tricky in a way that doesn't really pay off
for most people. Removing the first feature might even be necessary for
what I hope will ease the ugly. I don't see "Default generic arguments"
being easy to reason about alongside the first feature.

TJ

···

On Tue, Oct 11, 2016 at 9:30 PM, Robert Widmann via swift-evolution < swift-evolution@swift.org> wrote:

+1. I don't use this feature at all and (by extension) don't think there
are many situations where it's useful.

~Robert Widmann

2016/10/11 18:03、Slava Pestov via swift-evolution <
swift-evolution@swift.org> のメッセージ:

> I could if there’s interest. Since we intend on maintaining source
compatibility, it will not result in a simpler implementation, though,
since we’ll need to keep the old code path around for Swift 3 mode. Still
worth it?
>
> Slava
>
>> On Oct 11, 2016, at 1:58 PM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:
>>
>> I was reminded of this proposal which seems like an obvious win in
clarity. Still planning to submit it, Slava?
>>
>> ― Pyry
>>
>>> On 28 Jun 2016, at 21:13, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:
>>>
>>> on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:
>>>
>>>> Simpler interpretation of a reference to a generic type with no
>>>> arguments
>>>>
>>>> Proposal: SE-9999
>>>> <https://github.com/slavapestov/swift-evolution/blob/silly-proposals/
proposals/9999-simplify-unbound-generic-type.md>
>>>> Author: Slava Pestov <https://github.com/slavapestov>
>>>> Status: Awaiting review
>>>> Review manager: TBD
>>>> <https://github.com/slavapestov/swift-evolution/tree/silly-proposals/
proposals#introduction>Introduction
>>>>
>>>> This proposal cleans up the semantics of a reference to a generic type
>>>> when no generic arguments are applied.
>>>>
>>>> Swift-evolution thread: Discussion thread topic for that proposal
>>>> <http://news.gmane.org/gmane.comp.lang.swift.evolution>
>>>> <https://github.com/slavapestov/swift-evolution/tree/silly-proposals/
proposals#motivation>Motivation
>>>>
>>>> Right now, we allow a generic type to be referenced with no generic
>>>> arguments applied in a handful of special cases. The two primary rules
>>>> here are the following:
>>>>
>>>> If the scope from which the reference is made is nested inside the
>>>> definition of the type or an extension thereof, omitting generic
>>>> arguments just means to implicitly apply the arguments from context.
>>>>
>>>> For example,
>>>>
>>>> struct GenericBox<Contents> {
>>>> let contents: Contents
>>>>
>>>> // Equivalent to: func clone() -> GenericBox<Contents>
>>>> func clone() -> GenericBox {
>>>> return GenericBox(contents: contents)
>>>> }
>>>> }
>>>>
>>>> extension GenericBox {
>>>> func print() {
>>>> // Equivalent to: let cloned: GenericBox<Contents>
>>>> let cloned: GenericBox = clone()
>>>> print(cloned.contents)
>>>> }
>>>> }
>>>> If the type is referenced from an unrelated scope, we attempt to infer
>>>> the generic parameters.
>>>>
>>>> For example,
>>>>
>>>> func makeABox() -> GenericBox<Int> {
>>>> // Equivalent to: GenericBox<Int>(contents: 123)
>>>> return GenericBox(contents: 123)
>>>> }
>>>> The problem appears when the user expects the second behavior, but
>>>> instead encounters the first. For example, the following does not type
>>>> check:
>>>>
>>>> extension GenericBox {
>>>>
>>>> func transform<T>(f: Contents -> T) -> GenericBox<T> {
>>>> // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
>>>> // inferring the type parameter
>>>> return GenericBox(contents: f(contents))
>>>> }
>>>> }
>>>> <https://github.com/slavapestov/swift-evolution/tree/silly-proposals/
proposals#proposed-solution>Proposed
>>>> solution
>>>>
>>>> The proposed solution is to remove the first rule altogether. If the
>>>> generic parameters cannot be inferred from context, they must be
>>>> specified explicitly with the usual Type<Args...> syntax.
>>>
>>> SGTM. I've always found this shorthand to be somewhat surprising,
>>> including in C++ where (IIUC) it originated.
>>>
>>>
>>> --
>>> Dave
>>
>
> _______________________________________________
> 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


(Dave Abrahams) #18

I could if there’s interest. Since we intend on maintaining source
compatibility, it will not result in a simpler implementation, though,
since we’ll need to keep the old code path around for Swift 3
mode. Still worth it?

IIUC it especially makes sense in light of
https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md
Doesn't that make Self a perfect substitute for the use-case your
proposal eliminates?

···

on Tue Oct 11 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Slava

On Oct 11, 2016, at 1:58 PM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

I was reminded of this proposal which seems like an obvious win in clarity. Still planning to

submit it, Slava?

— Pyry

On 28 Jun 2016, at 21:13, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no
arguments

Proposal: SE-9999
<https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules
here are the following:

If the scope from which the reference is made is nested inside the
definition of the type or an extension thereof, omitting generic
arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
let contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>
func clone() -> GenericBox {
  return GenericBox(contents: contents)
}
}

extension GenericBox {
func print() {
  // Equivalent to: let cloned: GenericBox<Contents>
  let cloned: GenericBox = clone()
  print(cloned.contents)
}
}
If the type is referenced from an unrelated scope, we attempt to infer
the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
// Equivalent to: GenericBox<Int>(contents: 123)
return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

func transform<T>(f: Contents -> T) -> GenericBox<T> {
  // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
  // inferring the type parameter
  return GenericBox(contents: f(contents))
}
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be
specified explicitly with the usual Type<Args...> syntax.

SGTM. I've always found this shorthand to be somewhat surprising,
including in C++ where (IIUC) it originated.

--
Dave

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

--
-Dave


(Adrian Kashivskyy) #19

I vote to incorporate it somehow into the “Universal Self <https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md>” proposal.

– Adrian


(Haravikk) #20

I'm a +1 on the basis that I wasn't even aware of the first case, so have never used it! Although I've used clone methods and such in the past, I've always done so with the generics specified so never noticed that I could omit them, however I do frequently run into the unexpected case that this proposal seeks to solve.

···

On 11 Oct 2016, at 21:58, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

I was reminded of this proposal which seems like an obvious win in clarity. Still planning to submit it, Slava?

— Pyry

On 28 Jun 2016, at 21:13, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Thu Jun 23 2016, Slava Pestov <swift-evolution@swift.org> wrote:

Simpler interpretation of a reference to a generic type with no
arguments

Proposal: SE-9999
<https://github.com/slavapestov/swift-evolution/blob/silly-proposals/proposals/9999-simplify-unbound-generic-type.md>
Author: Slava Pestov <https://github.com/slavapestov>
Status: Awaiting review
Review manager: TBD
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#introduction>Introduction

This proposal cleans up the semantics of a reference to a generic type
when no generic arguments are applied.

Swift-evolution thread: Discussion thread topic for that proposal
<http://news.gmane.org/gmane.comp.lang.swift.evolution>
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#motivation>Motivation

Right now, we allow a generic type to be referenced with no generic
arguments applied in a handful of special cases. The two primary rules
here are the following:

If the scope from which the reference is made is nested inside the
definition of the type or an extension thereof, omitting generic
arguments just means to implicitly apply the arguments from context.

For example,

struct GenericBox<Contents> {
let contents: Contents

// Equivalent to: func clone() -> GenericBox<Contents>
func clone() -> GenericBox {
  return GenericBox(contents: contents)
}
}

extension GenericBox {
func print() {
  // Equivalent to: let cloned: GenericBox<Contents>
  let cloned: GenericBox = clone()
  print(cloned.contents)
}
}
If the type is referenced from an unrelated scope, we attempt to infer
the generic parameters.

For example,

func makeABox() -> GenericBox<Int> {
// Equivalent to: GenericBox<Int>(contents: 123)
return GenericBox(contents: 123)
}
The problem appears when the user expects the second behavior, but
instead encounters the first. For example, the following does not type
check:

extension GenericBox {

func transform<T>(f: Contents -> T) -> GenericBox<T> {
  // We resolve 'GenericBox' as 'GenericBox<Contents>', rather than
  // inferring the type parameter
  return GenericBox(contents: f(contents))
}
}
<https://github.com/slavapestov/swift-evolution/tree/silly-proposals/proposals#proposed-solution>Proposed
solution

The proposed solution is to remove the first rule altogether. If the
generic parameters cannot be inferred from context, they must be
specified explicitly with the usual Type<Args...> syntax.

SGTM. I've always found this shorthand to be somewhat surprising,
including in C++ where (IIUC) it originated.

--
Dave

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