[Pitch or bug?] Expanding the capability of `.` prefixed lookup


(Matt Gallagher) #1

Super short summary:

I think a function argument or right-hand-side expression prefixed with `.` should allow access to *any* static member on the expected type, dropping the existing limitations of this syntax.

Detail:

At the moment in Swift, you can use a `.` (period or dot) prefix to perform a scoped lookup of static vars and funcs on the expected type, if those static vars or funcs return that type.

For example:

  // If we have a type `SomeNontrivialTypeName`
  struct SomeNontrivialTypeName {
     // With a static function returning `SomeNontrivialTypeName`
     static func a() -> SomeNontrivialTypeName
  }
  
  // And a function that requires a `SomeNontrivialTypeName` parameter
  func f(a: SomeNontrivialTypeName)
  
  // We can call the function like this:
  f(a: .a())

The `.` prefix allows us to omit the typename `SomeNontrivialTypeName`; since the parameter already expects `SomeNontrivialTypeName`, the `.` already implies lookup in the list of static func/vars for `SomeNontrivialTypeName`.

The purpose is syntactic efficiency and it's used to great extent across a wide range of Swift/AppKit/Foundation interfaces for enum-like value lookups. It lets us have very simple names that won't clash with names in the global namespace because they're not in the global namespace – and yet, they're only a single `.` more syntactic overhead.

Unfortunately, there is no extendability. This approach will look up only static vars or funcs that immediately return the expected type and you can't transform the result – it's one function and done. For example, if `SomeNontrivialTypeName` has a fluent-style interface (i.e. an interface where instance methods return mutated `self` or new instances of `SomeNontrivialTypeName`):

  extension SomeNontrivialTypeName {
    func addThings() -> SomeNontrivialTypeName
  }

trying to append this function won't work, even though the return type remains correct:

  f(a: .a().addThings())

This fails and claims that we've forgotten to provide a parameter (!).

A completely different kind of transformation might go via a different type

  extension SomeNontrivialTypeName {
    static func another() -> AnotherType
  }
  
  struct AnotherType {
    func back() -> SomeNontrivialTypeName
  }

It would be nice to be able to use this "there-and-back-again" transformation:

  f(a: .another().back())

But it also won't work.

I realize that this is a point about minor syntactic efficiency. Yes, you could simply write:

  f(a: SomeNontrivialTypeName.another().back())

but it's clunky and the type name gets in the way.

There's also an element of consistency. Swift already lets us look up static functions in this way – but:
  
  * only functions that return the expected type
  * and we can't *use* the function result ourselves, it must be immediately yielded to the parameter or left-hand-side

Seems more than a little strange.

Anyone else care or have thoughts on this point?

Regards,
Matt Gallagher.


(Joe Groff) #2

I've also wanted this. It seems like a straightforward extension of the existing feature, which already has to set up a constraint system dependent on the contextual type, but which happens to be artificially constrained to adding only one step of member lookup or of member lookup followed by a call of that member to the system.

-Joe

···

On Jun 29, 2017, at 5:19 AM, Matt Gallagher via swift-evolution <swift-evolution@swift.org> wrote:

Super short summary:

I think a function argument or right-hand-side expression prefixed with `.` should allow access to *any* static member on the expected type, dropping the existing limitations of this syntax.

Detail:

At the moment in Swift, you can use a `.` (period or dot) prefix to perform a scoped lookup of static vars and funcs on the expected type, if those static vars or funcs return that type.

For example:

  // If we have a type `SomeNontrivialTypeName`
  struct SomeNontrivialTypeName {
     // With a static function returning `SomeNontrivialTypeName`
     static func a() -> SomeNontrivialTypeName
  }
  
  // And a function that requires a `SomeNontrivialTypeName` parameter
  func f(a: SomeNontrivialTypeName)
  
  // We can call the function like this:
  f(a: .a())

The `.` prefix allows us to omit the typename `SomeNontrivialTypeName`; since the parameter already expects `SomeNontrivialTypeName`, the `.` already implies lookup in the list of static func/vars for `SomeNontrivialTypeName`.

The purpose is syntactic efficiency and it's used to great extent across a wide range of Swift/AppKit/Foundation interfaces for enum-like value lookups. It lets us have very simple names that won't clash with names in the global namespace because they're not in the global namespace – and yet, they're only a single `.` more syntactic overhead.

Unfortunately, there is no extendability. This approach will look up only static vars or funcs that immediately return the expected type and you can't transform the result – it's one function and done. For example, if `SomeNontrivialTypeName` has a fluent-style interface (i.e. an interface where instance methods return mutated `self` or new instances of `SomeNontrivialTypeName`):

  extension SomeNontrivialTypeName {
    func addThings() -> SomeNontrivialTypeName
  }

trying to append this function won't work, even though the return type remains correct:

  f(a: .a().addThings())

This fails and claims that we've forgotten to provide a parameter (!).

A completely different kind of transformation might go via a different type

  extension SomeNontrivialTypeName {
    static func another() -> AnotherType
  }
  
  struct AnotherType {
    func back() -> SomeNontrivialTypeName
  }

It would be nice to be able to use this "there-and-back-again" transformation:

  f(a: .another().back())

But it also won't work.

I realize that this is a point about minor syntactic efficiency. Yes, you could simply write:

  f(a: SomeNontrivialTypeName.another().back())

but it's clunky and the type name gets in the way.

There's also an element of consistency. Swift already lets us look up static functions in this way – but:
  
  * only functions that return the expected type
  * and we can't *use* the function result ourselves, it must be immediately yielded to the parameter or left-hand-side

Seems more than a little strange.

Anyone else care or have thoughts on this point?


(Jarod Long) #3

I'd be happy to see this enhancement as well.

For what it's worth, a real case that I've wanted to use this for is modifying named colors, as in:

`view.backgroundColor = .blue.withAlphaComponent(0.5)`

Not a major inconvenience for sure, but it would be nice if this worked.

Jarod

···

On Jun 29, 2017, 09:54 -0700, Joe Groff via swift-evolution <swift-evolution@swift.org>, wrote:

> On Jun 29, 2017, at 5:19 AM, Matt Gallagher via swift-evolution <swift-evolution@swift.org> wrote:
>
> Super short summary:
>
> I think a function argument or right-hand-side expression prefixed with `.` should allow access to *any* static member on the expected type, dropping the existing limitations of this syntax.
>
> Detail:
>
> At the moment in Swift, you can use a `.` (period or dot) prefix to perform a scoped lookup of static vars and funcs on the expected type, if those static vars or funcs return that type.
>
> For example:
>
> // If we have a type `SomeNontrivialTypeName`
> struct SomeNontrivialTypeName {
> // With a static function returning `SomeNontrivialTypeName`
> static func a() -> SomeNontrivialTypeName
> }
>
> // And a function that requires a `SomeNontrivialTypeName` parameter
> func f(a: SomeNontrivialTypeName)
>
> // We can call the function like this:
> f(a: .a())
>
> The `.` prefix allows us to omit the typename `SomeNontrivialTypeName`; since the parameter already expects `SomeNontrivialTypeName`, the `.` already implies lookup in the list of static func/vars for `SomeNontrivialTypeName`.
>
> The purpose is syntactic efficiency and it's used to great extent across a wide range of Swift/AppKit/Foundation interfaces for enum-like value lookups. It lets us have very simple names that won't clash with names in the global namespace because they're not in the global namespace – and yet, they're only a single `.` more syntactic overhead.
>
> Unfortunately, there is no extendability. This approach will look up only static vars or funcs that immediately return the expected type and you can't transform the result – it's one function and done. For example, if `SomeNontrivialTypeName` has a fluent-style interface (i.e. an interface where instance methods return mutated `self` or new instances of `SomeNontrivialTypeName`):
>
> extension SomeNontrivialTypeName {
> func addThings() -> SomeNontrivialTypeName
> }
>
> trying to append this function won't work, even though the return type remains correct:
>
> f(a: .a().addThings())
>
> This fails and claims that we've forgotten to provide a parameter (!).
>
> A completely different kind of transformation might go via a different type
>
> extension SomeNontrivialTypeName {
> static func another() -> AnotherType
> }
>
> struct AnotherType {
> func back() -> SomeNontrivialTypeName
> }
>
> It would be nice to be able to use this "there-and-back-again" transformation:
>
> f(a: .another().back())
>
> But it also won't work.
>
> I realize that this is a point about minor syntactic efficiency. Yes, you could simply write:
>
> f(a: SomeNontrivialTypeName.another().back())
>
> but it's clunky and the type name gets in the way.
>
> There's also an element of consistency. Swift already lets us look up static functions in this way – but:
>
> * only functions that return the expected type
> * and we can't *use* the function result ourselves, it must be immediately yielded to the parameter or left-hand-side
>
> Seems more than a little strange.
>
> Anyone else care or have thoughts on this point?

I've also wanted this. It seems like a straightforward extension of the existing feature, which already has to set up a constraint system dependent on the contextual type, but which happens to be artificially constrained to adding only one step of member lookup or of member lookup followed by a call of that member to the system.

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