# Allow chained member references in implicit member expressions

**URL:** <https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829>\
**Category:** Pitches\
**Created:** [January 21, 2020, 11:58pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829 "2020-01-21T23:58:03Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 21, 2020, 11:58pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/1 "2020-01-21T23:58:03Z")

</div>

## Introduction

Following up on the [discussion thread](https://forums.swift.org/t/enabling-implicit-member-references-with-nested-members/32713) from the other day, I propose that we extend implicit member syntax (a.k.a. "leading dot syntax") to allow for chained member references rather than just a single level.

## Motivation

When the type of an expression is implied by the context, Swift allows developers to use what is formally referred to as an "implicit member expression," sometimes referred to as "leading dot syntax":

```swift
class C {
    static let zero = C(0)
    var x: Int

    init(_ x: Int) {
        self.x = x
    }
}

func f(_ c: C) {
    print(c.x)
}

f(.zero) // prints '0'

```

However, attempting to use this with a chain of member references fails:

```swift
extension C {
    var incremented: C {
        return C(self.x + 1)
    }
}

f(.zero.incremented) // Error: Type of expression is ambiguous without more context

```

On `master`, the new diagnostic system has improved the produced error:

```swift

f(.zero.incremented) // Error: Cannot infer contextual base in reference to member 'zero'

```

but the same problem persists. This error breaks the mental model that many users likely have for implicit member syntax, which boils down to a simple lexical omission of the type name in contexts where the type is clear. I.e., users expect that writing:

```swift
let one: C = .zero.incremented

```

is just the same as writing

```swift
let one = C.zero.incremented

```

This issue arises in practice with any type that offers "modifier" methods that vend updated instances according to some rule. For example, `UIColor` offers the `withAlphaComponent(_:)` modifier for constructing new colors, which cannot be used with implicit member syntax:

```swift
let milky: UIColor = .white.withAlphaComponent(0.5) // error

```

## Proposed solution

Type inference would be improved in order to be able to handle multiple chained member accesses. The type of the resulting expression would be constrained to match the contextual type.

## Detailed design

This proposal would provide the model mentioned earlier for implicit member expressions: anywhere that a contextual type `T` can be inferred, writing

```swift
.member1.member2.(...).memberN

```

Will behave exactly as if the user had written:

```swift
T.member1.member2.(...).memberN

```

Further, if `T` is the contextually inferred type but `memberN` has non-convertible type `R`, a diagnostic of the form:

```swift
Error: Cannot convert value of type 'R' to expected type 'T'

```

will be produced. The exact form of the diagnostic will depend on how `T` was contextually inferred (e.g. as an argument, as an explicit type annotation, etc.).

## Source compatibility

This is a purely additive change and does not have any effect on source compatibility.

## Effect on ABI stability

This change is frontend only and would not impact ABI.

## Effect on API resilience

This is not an API-level change and would not impact resilience.

---

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [January 22, 2020, 12:16am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/2 "2020-01-22T00:16:58Z")

</div>

Makes sense to me, and matches my mental model of how things should work!

---

<div class="post-metadata">

**Author:** ![anandabits](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anandabits/32/355_2.png) [@anandabits](https://forums.swift.org/u/anandabits)\
**Post date:** [January 22, 2020, 12:35am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/3 "2020-01-22T00:35:16Z")

</div>

This is a nice improvement. Thank you for working on it!

---

<div class="post-metadata">

**Author:** ![Paul\_Cantrell](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paul_cantrell/32/16764_2.png) [@Paul\_Cantrell](https://forums.swift.org/u/Paul_Cantrell)\
**Post date:** [January 22, 2020, 1:32am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/4 "2020-01-22T01:32:58Z")

</div>

Entirely sensible. TBH, I’m surprised the language doesn’t already do this.

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [January 22, 2020, 2:16am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/5 "2020-01-22T02:16:31Z")

</div>

Also +1 from me, I run into this every time I need to replace a static usage with one of its properties, like the `UIColor` example above.

---

<div class="post-metadata">

**Author:** ![tkrajacic](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tkrajacic/32/1173_2.png) [@tkrajacic](https://forums.swift.org/u/tkrajacic)\
**Post date:** [January 22, 2020, 7:07am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/6 "2020-01-22T07:07:04Z")

</div>

+1 The color example comes up for me a lot and it seems like this should work exactly as you propose. Thank you for looking into this!

---

<div class="post-metadata">

**Author:** ![dlbuckley](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dlbuckley/32/14986_2.png) [@dlbuckley](https://forums.swift.org/u/dlbuckley)\
**Post date:** [January 22, 2020, 8:57am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/7 "2020-01-22T08:57:50Z")

</div>

+1 This is one of those things that improves the consistency and ergonomics of the language. I would be happy to see a full proposal for this.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [January 22, 2020, 7:39pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/8 "2020-01-22T19:39:24Z")

</div>

This would be a great improvement. With generic types, should it also be possible for members looked up along the chain to have different generic arguments? You could for instance have something like this:

```swift
struct Foo<T> { }

extension Foo where T == Int { static var a: Foo<String> }
extension Foo where T == String { var b: Foo<Double> }
extension Foo where T == Double { var c: Foo<Float> }

func anyFoo<T>(_: Foo<T>) {}
func floatFoo(_: Foo<Float>) {}

anyFoo(.a.b.c)
floatFoo(.a.b.c)

```

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 22, 2020, 8:01pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/9 "2020-01-22T20:01:27Z")

</div>

@Joe_Groff I'm assuming `b` and `c` were not meant to be static here, and that the first extension was meant to be `where T == Float`?

If I'm understanding correctly, then I would expect the following:

```swift
anyFoo(.a.b.c) // Error: Generic parameter 'T' could not be inferred.
floatFoo(.a.b.c) // OK

```

In general, I'm not proposing any constraints on any members of the chain besides the last. So the following would work just fine as well:

```swift
struct A {
    static var b = B()
}

struct B {
    var c = C()
}

struct C {
    var a = A()
}

func f(_: A) {}

f(.b.c.a) // OK

```

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 22, 2020, 8:11pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/10 "2020-01-22T20:11:01Z")

</div>

Love it. ❤ And the only question that I had was answered the example in the post above this one.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 22, 2020, 8:13pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/11 "2020-01-22T20:13:30Z")

</div>

I'll be sure to include that example when this eventually ends up as a full proposal! 🙂

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [January 22, 2020, 8:25pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/12 "2020-01-22T20:25:26Z")

</div>

Ah, I did mean for `b` and `c` to be instance properties, but I intentionally meant for the the first extension to be `T == Int`. Currently, we do allow for implicit member lookup to guide generic argument inference. This compiles:

```swift
struct Foo<T> {}

extension Foo where T == Int { static var x = Foo<Int>() }

func foo<T>(_: Foo<T>) { print(T.self) }

foo(.x)

```

and so it seems reasonable that multiple-member chains could guide generic argument inference too.

---

<div class="post-metadata">

**Author:** ![DevAndArtist](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/devandartist/32/287_2.png) [@DevAndArtist](https://forums.swift.org/u/DevAndArtist)\
**Post date:** [January 22, 2020, 8:31pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/13 "2020-01-22T20:31:07Z")

</div>

> [@Joe\_Groff](#):
>
> Currently, we do allow for implicit member lookup to guide generic argument inference.

Is this really what‘s happening here? Isn‘t the where clause in that particular example ignored by the compiler similar to how it‘s definitely is ignored with associated type inference. I really dislike this latter part of the language. :(

> [@What kind of magic behavior is this?](https://forums.swift.org/t/what-kind-of-magic-behavior-is-this/22014):
>
> I was playing around with some API design where I accidentally discovered this magical behavior. Why is the compiler allowed to use the protocol extension with a where clause as default implementation for the missing protocol requirements? protocol Test { associatedtype Something var something: Something { get set } } extension Test where Something == Int { var something: Something { get { return 42 } set { } } } struct S: Test {} print(S().something) // prints 42 Is this a…

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [January 22, 2020, 8:37pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/14 "2020-01-22T20:37:23Z")

</div>

I don't see how the where clause is being "ignored by the compiler" in either case. Until the argument that the where clause applies to can be deduced, the type checker has to consider potential members that may be part of the generic type depending on what arguments end up being picked.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 22, 2020, 8:37pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/15 "2020-01-22T20:37:31Z")

</div>

Ah, interesting—didn't realize that! I think then I would expect the following errors:

```swift
anyFoo(.a.b.c) // Error: Cannot convert value of type 'Foo<Float>' to 'Foo<Int>'
floatFoo(.a.b.c) // Error: Static property 'a' requires that 'Float' and 'Int' be equivalent.

```

I agree that given the current behavior it seems like multiple-member chains should also guide the generic argument inference.

---

<div class="post-metadata">

**Author:** ![nnnnnnnn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nnnnnnnn/32/264_2.png) [@nnnnnnnn](https://forums.swift.org/u/nnnnnnnn)\
**Post date:** [January 22, 2020, 9:55pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/16 "2020-01-22T21:55:52Z")

</div>

> [@Jumhyn](#):
>
> ```swift
> .member1.member2.(...).memberN
> 
> ```

Are there examples that motivate allowing heterogenous types for everything between the implied `T` and `memberN`? All the times I've ever wanted to use this, it's been because I'm using some kind of fluent interface, so the properties are `T` across the board. This includes things like the examples in the proposal as well as `Font` in SwiftUI, and probably others.

It seems like opening implicit member expressions up all the way adds quite a bit of cognitive load for those reading the code, without a huge benefit.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 22, 2020, 10:08pm UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/17 "2020-01-22T22:08:13Z")

</div>

Yeah, I know what you mean. I did think about whether the whole chain should be constrained to `T`, but ultimately felt like that conflicted with my mental model of what is going on with implicit member expressions (alluded to in the proposal): in cases where the type of the expression is "obvious", you can omit the type name if you're just accessing a member off that type.

Admittedly, that's somewhat complicated by the generic parameter inference since it's not "obvious" what the type of the expression is without considering the chain, so as I start looking at implementing this I'll have to think about what makes sense in those cases.

In the brief examples written here I see what you mean about the cognitive load, but I think that in most cases with well-named APIs the meaning will still be pretty clear, e.g.:

```swift
struct Font {
    var family: FontFamily { ... }
    static var systemDefault: Font
}

struct FontFamily {
    func boldFontOfSize(_ size: CGFloat) -> Font { ... }
}

// ...

label.font = .systemDefault.family.boldFontOfSize(12.0)

```

Now, I can't really think of a real-world situation in which I've wanted the feature to take this form, but I also couldn't really come up with a compelling reason to add what otherwise felt like an arbitrary restriction. I don't have super strong feelings either way, even though allowing heterogeneously-typed chains fits better with my expectations!

---

<div class="post-metadata">

**Author:** ![rintaro](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rintaro/32/568_2.png) [@rintaro](https://forums.swift.org/u/rintaro)\
**Post date:** [January 23, 2020, 12:39am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/18 "2020-01-23T00:39:46Z")

</div>

> [@Jumhyn](#):
>
> ```swift
> .member1.member2.(...).memberN
> 
> ```
> 
> Will behave exactly as if the user had written:
> 
> ```swift
> T.member1.member2.(...).memberN
> 
> ```

Does this mean that calling instance methods by implicit-member-expressions will be supported?:

```swift
struct MyStruct {
  func modify(by: Int) -> MyStruct { ... }
}

func test(base: MyStruct, arg: Int) {
  let _: MyStruct = MyStruct.modify(base)(by: arg)
  let _: MyStruct = .modify(base)(by: arg)
}

```

This is gross, IMO.

Another concern is for code-completion. Currently, for `.` completion, Xcode only shows enum cases, static members, and initializers which results `T`. However, after this language change, we need to show every members except for subscripts after the initial '`.`'. Of course we can prioritize enum cases and matching static variables in the Xcode UI. But users may confuse when they see a bunch of looks-like-unrelated members in the completion window.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 23, 2020, 1:10am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/19 "2020-01-23T01:10:53Z")

</div>

> [@rintaro](#):
>
> Does this mean that calling instance methods by implicit-member-expressions will be supported?:

Good callout. My initial reaction is yes, that form should be supported by implicit member syntax. I actually think the two versions you've written out there read about as well as one another. This feels like another example like the ones from my original post where difficulty results from the use of generic/placeholder variable and method names—the weirdness isn't really exaggerated (to me) by omitting the type name. But I like this example because it's a concrete illustration of a situation that will arise frequently, but which could be avoided if the "all types along the chain must be the same" rule were adopted.

Without that rule, though, I think carving out an exception just for instance methods is not super strongly motivated. Formally, they _are_ members of the type, and static methods are otherwise supported by implicit member syntax. It feels like it would complicate what is otherwise a simple model.

As far as code completion goes, I think prioritizing members which produce the contextual type still makes sense, since the chain will be required to end in a value of that type, but yes, this would open up the completion options to all static members of the type.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 23, 2020, 1:23am UTC](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829/20 "2020-01-23T01:23:07Z")

</div>

A further thought along the lines of the question that @Joe_Groff raised is whether _down-chain_ members can influence generic argument inference, e.g.:

```swift
struct Foo<T> {}

extension Foo where T == Int { static var a: Foo<Float> }
extension Foo where T == String { static var a: Foo<Double> }
extension Foo where T == Double { var b: Foo<String> }

func anyFoo<T>(_ foo: Foo<T>) {}

anyFoo(.a.b) // anyFoo<String>, or bail out?

```

My initial thought is that it would be fine to let just the first member participate in generic argument inference, and let the above fail with an "ambiguous use of 'a'" error. It seems like allowing the whole chain to participate in inference could cause a performance blowup in pathological cases, though maybe this wouldn't be an issue in practice. Interested to know what others think!

[Next page](https://forums.swift.org/t/allow-chained-member-references-in-implicit-member-expressions/32829.md?page=2)
