# \[Pitch\] Type inference from default expressions

**URL:** <https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585>\
**Category:** Pitches\
**Created:** [February 24, 2022, 10:06pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585 "2022-02-24T22:06:27Z")\
**Posts on this page:** 14\
**Page:** 2

<div class="post-metadata">

**Author:** ![David\_Sweeris](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/david_sweeris/32/31302_2.png) [@David\_Sweeris](https://forums.swift.org/u/David_Sweeris)\
**Post date:** [March 1, 2022, 10:02pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/21 "2022-03-01T22:02:17Z")

</div>

FWIW, using the OP's `Box` examples, I think I can match the _call-site syntax_ with today's toolchain. This all works in an Xcode 13.2.1 playground...

> **Boilerplatey Stuff that Probably Doesn't Matter but I Included Anyway for the Sake of Completeness**
>
> ```swift
> enum Flag: String, CustomStringConvertible {
> case fragile
> case pastDue
> case damaged
> case flatRate
> var description: String { self.rawValue }
> }
> protocol Flags { var flags: [Flag] {get set} }
> struct DefaultFlags: Flags { var flags: [Flag] = [] }
> struct FlatRate: Flags { var flags: [Flag] = [.flatRate] }
> struct Damaged: Flags { var flags: [Flag] = [.damaged] }
> 
> enum ShippingFlag: String, CustomStringConvertible {
> case express
> var description: String { self.rawValue }
> }
> protocol ShippingFlags { var flags: [ShippingFlag] {get set} }
> struct DefaultShippingFlags: ShippingFlags { var flags: [ShippingFlag] = [] }
> struct Express: ShippingFlags { var flags: [ShippingFlag] = [.express] }
> 
> struct Dimensions {
> static let small = Dimensions(length: 1, width: 2, height: 3)
> static let medium = Dimensions(length: 1, width: 2, height: 3)
> static let large = Dimensions(length: 1, width: 2, height: 3)
> var length: Double
> var width: Double
> var height: Double
> }
> 
> ```

Existing "code-site" syntax:

```swift
struct StructBox<F: Flags> {
    var flags: F
    var dimensions: Dimensions
    
    init(dimensions: Dimensions, flags: F) {
        self.dimensions = dimensions
        self.flags = flags
    }
    func ship<F2: ShippingFlags>(_ f2: F2) -> [ShippingFlag] {
        f2.flags
    }
    func ship() -> [ShippingFlag] {
        ship(DefaultShippingFlags())
    }
}
extension StructBox where F == DefaultFlags {
    init(dimensions: Dimensions) {
        self.init(dimensions: dimensions, flags: DefaultFlags())
    }
}

enum EnumBox<F: Flags> {
    case regular(Dimensions, F)
    
    var flags: [Flag] {
        switch self {
        case .regular(_, let flags): return flags.flags
        }
    }
}
extension EnumBox where F == FlatRate {
    static func flatRate(_ dimensions: Dimensions) -> EnumBox {
        .regular(dimensions, FlatRate())
    }
}

```

And the resulting call-site code & output:

```swift
let structBox1 = StructBox(dimensions: .medium)
let structBox2 = StructBox(dimensions: .medium, flags: Damaged())
print(structBox1.flags.flags) // []
print(structBox2.flags.flags) // [damaged]
print(structBox1.ship()) // []
print(structBox1.ship(Express())) // [express]

let enumBox1: EnumBox = .flatRate(.large)
let enumBox2: EnumBox = .regular(.large, Damaged())
print(enumBox1.flags) // [flatRate]
print(enumBox2.flags) // [damaged]

```

IIUC, you're proposing that the following syntax would have the same behavior?

```swift
struct StructBox<F: Flags> {
    var flags: F
    var dimensions: Dimensions
    
    init(dimensions: Dimensions, flags: F = DefaultFlags()) {
        self.dimensions = dimensions
        self.flags = flags
    }
    func ship<F2: ShippingFlags>(_ f2: F2 = DefaultShippingFlags()) -> [ShippingFlag] {
        f2.flags
    }
}

enum EnumBox<F: Flags> {
    case regular(Dimensions, F)
    case flateRate(Dimensions, F = FlatRate())
    
    var flags: [Flag] {
        switch self {
        case .regular(_, let flags): return flags.flags
        case .flateRate(_, let flags): return flags.flags
        }
    }
}

```

I can see the advantage.

I can't help but wonder, though, if there are ways to accomplish the same thing in the OP's example, could this is another example of "convenient code-gen stuff that gets added to the compiler instead of being a use case for meta-programming"? I'm not saying this _is_ the case here, I'm just asking the question. Arguably my example just skates in as "working" due to a technicality... the OP's use case only had instantiating an enum, not switching on one, and I'm not sure if there's a way to handle that with the existing toolchain.

(Also, I didn't have time to even try @stephencelis's example use case.)

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [March 1, 2022, 10:14pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/22 "2022-03-01T22:14:31Z")

</div>

> [@David\_Sweeris](#):
>
> IIUC, you're proposing that the following syntax would have the same behavior?

Yes, that is what I'm proposing, the ergonomic improvements target declarations not the call site, call sites continue to work as before.

> [@David\_Sweeris](#):
>
> I can't help but wonder, though, if there are ways to accomplish the same thing in the OP's example, could this is another example of "convenient code-gen stuff that gets added to the compiler instead of being a use case for meta-programming"?

Theoretically speaking we could go that way and teach the compiler to synthesize all the declarations in different ways (conditional extensions, where clauses, and generic parameter replacement which is going to be unprecedented if we did), I just don't see any advantages of that approach versus the one proposed here. Doing so would also result in degraded user experience because synthesized code doesn't have code locations to anchor on.

Also note that all listed existing approaches add new overloads and the code synthesis approach is not going to help with that. This is a major disadvantage, because adding new overloads purely to default some parameters have negative effect on user experience as well as type-checker performance.

---

<div class="post-metadata">

**Author:** ![sveinhal](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sveinhal/32/3992_2.png) [@sveinhal](https://forums.swift.org/u/sveinhal)\
**Post date:** [March 2, 2022, 4:16pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/23 "2022-03-02T16:16:44Z")

</div>

Love it!

SwiftUI itself is littered with these declarations:

```swift
extension Section where Header == EmptyView { ... }
extension Section where Footer == EmptyView { ... }
extension Section where Header == EmptyView, Footer == EmptyView { ... }

```

… just to be able to afford the user with convenience inits with default headers and footers.

These declarations explode when you have a few generic parameters, and you want to provide default values for all combinations of them. I have exactly the same problem in my own code, often with SwiftUI `View`s, but in other types as well. Hundred of lines of boilerplate and repetitive copy-pasted code.

---

<div class="post-metadata">

**Author:** ![David\_Sweeris](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/david_sweeris/32/31302_2.png) [@David\_Sweeris](https://forums.swift.org/u/David_Sweeris)\
**Post date:** [March 2, 2022, 7:22pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/24 "2022-03-02T19:22:39Z")

</div>

> [@xedin](#):
>
> Theoretically speaking we could go that way and teach the compiler to synthesize all the declarations in different ways (conditional extensions, where clauses, and generic parameter replacement which is going to be unprecedented if we did), I just don't see any advantages of that approach versus the one proposed here. Doing so would also result in degraded user experience because synthesized code doesn't have code locations to anchor on.

Oh, I know you're not proposing that. I just keep seeing things that _could_ be implemented that way and it makes me wonder if there's a more general problem to get at is all.

> **Off-Topic Tangent Regarding the Type-Checker**
>
> > [@xedin](#):
> >
> > Also note that all listed existing approaches add new overloads and the code synthesis approach is not going to help with that. This is a major disadvantage, because adding new overloads purely to default some parameters have negative effect on user experience as well as type-checker performance.
> 
> I thought the big issue with type-checker performance was when you overloaded functions with different argument _types_, and that overloading functions with different numbers of arguments was fine. Is that not the case? Or is it just that it's only "fine" when compared to type-based overloading?

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [March 2, 2022, 7:30pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/25 "2022-03-02T19:30:06Z")

</div>

> [@David\_Sweeris](#):
>
> I thought the big issue with type-checker performance was when you overloaded functions with different argument _types_ , and that overloading functions with different numbers of arguments was fine. Is that not the case? Or is it just that it's only "fine" when compared to type-based overloading?

It depends - usually concretely typed overloads are fine but in situations relevant to this proposal overloads have to have the same labels plus generic types, which is the worst combination because type-checker cannot rule out any of such overloads upfront.

---

<div class="post-metadata">

**Author:** ![David\_Sweeris](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/david_sweeris/32/31302_2.png) [@David\_Sweeris](https://forums.swift.org/u/David_Sweeris)\
**Post date:** [March 2, 2022, 7:50pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/26 "2022-03-02T19:50:25Z")

</div>

Ah, got it, thanks

---

<div class="post-metadata">

**Author:** ![gwendal.roue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gwendal.roue/32/13992_2.png) [@gwendal.roue](https://forums.swift.org/u/gwendal.roue)\
**Post date:** [March 3, 2022, 4:26pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/27 "2022-03-03T16:26:38Z")

</div>

The feedback is very positive, and there is no doubt that the pitch addresses a common pain point. It greatly helps writing apis where one generic argument has one sensible default of one concrete type.

Then I read [Pitch: Allow functions with void-params to be called as void-functions](https://forums.swift.org/t/pitch-allow-functions-with-void-params-to-be-called-as-void-functions/55707).

It made me think that one way to generalize this "Type inference from default expressions" pitch is apis where one generic argument has multiple sensible defaults depending on the concrete type. For example, the api author would like the default value to be `()` when the generic type is `Void`, `42` for `Int`, and `"Hello World!"` for `String`, `DefaultFlags()` for `DefaultFlags`, etc.

This gives me one concern:

I can imagine one user, who is really (but unconsciously) looking after the generalized use case, but starts with this proposal, with success. The user thinks "Yeah! I can provide `DefaultFlags()` as a default value!". What he should have thought was "Yeah! I can provide `DefaultFlags()` as a default value _for `DefaultFlags`_", but he missed this subtlety (or maybe this subtlety is not a concern at this stage).

Later this user realizes the need to provide a different default value for a different concrete type. So he needs to refactor his api, and provide defaults values as we used to do before this pitch (with as many overloads as needed). Less sexy, but still workable. And the change is API-stable.

But what if the user's code has ABI-stability concerns? **Is is possible to opt-out of this pitch in an ABI stable way?**

```diff
 // Is this diff ABI-stable?
 struct Box<F: Flags> {
- init(flags: F = DefaultFlags()) { ... }
+ init(flags: F) { ... }
 }
+extension Box where F == DefaultFlags {
+ init() { self.init(DefaultFlags()) }
+}

```

If there is no way to opt-out of this pitch in an ABI-stable way, then it looks _very dangerous_ for users who have ABI-stability constraints.

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [March 3, 2022, 4:50pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/28 "2022-03-03T16:50:18Z")

</div>

> [@gwendal.roue](#):
>
> ```swift
> // Is this diff ABI-stable?
> struct Box<F: Flags> {
> - init(flags: F = DefaultFlags()) { ... }
> + init(flags: F) { ... }
> }
> +extension Box where F == DefaultFlags {
> + init() { self.init(DefaultFlags()) }
> +}
> 
> ```

Adding overloads (with appropriate availability) and adding/removing default arguments are both resilient changes. Default arguments are not ABI, and this pitch doesn't change that. We also have tools like the ABI checker to help library authors with ABI constraints ensure that they don't break ABI.

EDIT: The above diff would be fully source and ABI compatible if the new `init` is marked `@_alwaysEmitIntoClient`.

---

<div class="post-metadata">

**Author:** ![gwendal.roue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gwendal.roue/32/13992_2.png) [@gwendal.roue](https://forums.swift.org/u/gwendal.roue)\
**Post date:** [March 3, 2022, 4:57pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/29 "2022-03-03T16:57:08Z")

</div>

Thank you Holly. So you're confident that (at least) the stdlib won't fall in a pit because of this pitch.

> **aside about the abi checker**
>
> > We also have tools like the ABI checker to help library authors with ABI constraints ensure that they don't break ABI.
> 
> A google search for "Swift ABI checker" did not yield clear results. Is this checker available for developers who are not working on the stdlib?

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [March 3, 2022, 4:58pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/30 "2022-03-03T16:58:17Z")

</div>

> [@gwendal.roue](#):
>
> It made me think that one way to generalize this "Type inference from default expressions" pitch is apis where one generic argument has multiple sensible defaults depending on the concrete type. For example, the api author would like the default value to be `()` when the generic type is `Void` , `42` for `Int` , and `"Hello World!"` for `String` , `DefaultFlags()` for `DefaultFlags` , etc.

This is an interesting point, but I wouldn't call it a generalization of this pitch because it doesn't allow for this default concrete type to be inferred. Providing a default depending on the concrete type means the concrete type needs to be supplied first in order for the default value to be chosen. This pitch suggests the opposite - if a default argument is used, type inference will use its type for the generic argument. This prevents programmers form having to specify that concrete type at the call-site, e.g. via type annotation.

---

<div class="post-metadata">

**Author:** ![gwendal.roue](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gwendal.roue/32/13992_2.png) [@gwendal.roue](https://forums.swift.org/u/gwendal.roue)\
**Post date:** [March 3, 2022, 5:01pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/31 "2022-03-03T17:01:26Z")

</div>

You are correct. My point was about the risk that the two use cases are conflated by the api developer, because they have identical effects (when / as long as) a single concrete type is involved.

When the api developer is wrong, we end up, after a few rounds of api evolution, with code such as below, where the privileged/special position of `DefaultFlags` has no meaning at all:

```swift
struct Box<F: Flags> {
  // Symmetry breakage for DefaultFlags, which should
  // be handled as OtherFlags and YetAnotherFlags below.
  init(_ flags: F = DefaultFlags()) { ... }
}
extension Box where F == OtherFlags {
  init() { self.init(OtherFlags()) }
}
extension Box where F == YetAnotherFlags {
  init() { self.init(YetAnotherFlags()) }
}

```

I know that we should not negatively judge language changes because some people may misuse them. Yet the risk for confusion is high, here, and I think it is better if it is expressed in this pitch.

[I am guilty this confusion myself!](https://forums.swift.org/t/pitch-allow-functions-with-void-params-to-be-called-as-void-functions/55707/11)

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [March 3, 2022, 5:27pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/32 "2022-03-03T17:27:20Z")

</div>

> **Answering your ABI checker question**
>
> > [@gwendal.roue](#):
> >
> > A google search for "Swift ABI checker" did not yield clear results. Is this checker available for developers who are not working on the stdlib?
> 
> Yep! The `swift-api-digester` tool has options to inspect and check ABI. It's included in the toolchain; if you have one installed, running `swift api-digester --help` on the command line should show you the various flags. A Google or Swift Forum search of "swift-api-digester" turns up more useful results.

---

<div class="post-metadata">

**Author:** ![xedin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xedin/32/309_2.png) [@xedin](https://forums.swift.org/u/xedin)\
**Post date:** [March 3, 2022, 6:36pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/33 "2022-03-03T18:36:33Z")

</div>

The biggest question for me here is whether it's sensible for API authors to even provide a default in this case or instead require that argument is always specified explicitly if there are multiple possible choices, like with this `Void` example for [Pitch: Allow functions with void-params to be called as void-functions](https://forums.swift.org/t/pitch-allow-functions-with-void-params-to-be-called-as-void-functions/55707) - the choice, it seems, is to provide default only if there is no output to improve ergonomics of that API, it doesn't means that `send()` _should_ be supported for every possible `Output`.

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [March 15, 2022, 10:09pm UTC](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585/34 "2022-03-15T22:09:12Z")

</div>

Hey all,

Thanks for the great discussion in the pitch. We've gone ahead and scheduled a review for this proposal as [SE-0347](https://github.com/apple/swift-evolution/blob/main/proposals/0347-type-inference-from-default-exprs.md) starting on March 22nd.

Doug

[Previous page](https://forums.swift.org/t/pitch-type-inference-from-default-expressions/55585.md?page=1)
