SE-0315: Placeholder types

One of the reasons that placeholders for generic bases was left out of this proposal is that I myself am more skeptical of the utility there, and this point raises another useful objection. Currently, since standalone placeholders are disallowed and generic bases must be specified, it is (hopefully) clear to the author that any usage of placeholders in property signatures is a commitment to a specific concrete type.

That is, if generic bases were allowed to be placeholders, the property declared as:

let publisher: _<Int, _> = Just(makeValue()).setFailureType(to: Error.self).eraseToAnyPublisher()

will have its type visible to clients as AnyPublisher<Int, Error>, and it could not be later changed to have type PassthroughSubject<Int, Error> without an ABI/API break, whereas (with the proper additional opaque type features) a property declared as:

let publisher: some Publisher where .Output == Int, .Failure == Error = Just(makeValue()).setFailureType(to: Error.self)

could freely change its concrete type later on as long as it obeyed the some Publisher contract.

Of course, neither of the forms above are valid today (or under this proposal). The former must be written as:

let publisher: AnyPublisher<Int, _> = Just(makeValue()).setFailureType(to: Error.self).eraseToAnyPublisher()

and the second can't even be properly expressed.

@hborla's succinct summary is, IMO, a great distillation of the scope of this feature:

This feature doesn't attempt to introduce any new fundamental capabilities/expressive power to the type system (as opaque types did), it only provides more specificity in how users can direct type inference using existing type system capabilities.

I suspect that a nomenclature shift to "inferred type" would help greatly with the understanding of this feature.

Does that appropriately address your questions/concerns, @Philippe_Hausler?

5 Likes

100% Thanks for the clarifications.

1 Like
  • What is your evaluation of the proposal?
    +1 - this feels like a natural extension to the language

  • Is the problem being addressed significant enough to warrant a change to Swift?
    I think so - as the proposal points out, there are a number of situations today where the language requires writing out complex types because a single generic parameter or element type couldn't be inferred from context.

  • Does this proposal fit well with the feel and direction of Swift?

Yes, the spelling fits with the existing uses of _ IMO

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    I followed the latest pitch thread and read the final proposal
  • What is your evaluation of the proposal? / Is the problem being addressed significant enough to warrant a change to Swift? / Does this proposal fit well with the feel and direction of Swift?

This proposal seems like a natural progression of Swift's type inference. It also allows Swift to be more succinct without making it harder to read or write.

I appreciate the simple examples you included to show the placeholder type in action and its multiple possible uses. These made this proposal easy to understand for a proposal-reading-beginner like myself.

At first, I thought "placeholder type" was going to be about editor placeholders.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

In-depth study

2 Likes

Great idea. One comment, however:

In a compiled interface, [placeholder types] are replaced by whatever type the type checker fills in for the placeholder.

Unless you've done something very fancy, this won't actually be true for the bodies of inlinable functions, which are emitted using relatively crude textual transformations from the source code, rather than by re-printing them from the typechecked AST. The same goes for default arguments. So if you use a placeholder in the body of an inlinable function or in a default argument, older compilers will choke on the module interface.

(This kind of thing is why module interfaces only guarantee forwards compatibility, so it's not a serious problem with the feature; I just wanted to note that the text here is inaccurate.)

5 Likes

Ooh, thank you! That’s an edge case I was unaware of. I've opened a pull request here to clarify the wording.

1 Like

This seems like a potential footgun for authors of libraries that may be inlined by older compilers, and who would know to even test this? Any way we can mitigate this with diagnostics? Maybe a warning when used in inlinable function bodies and default arguments that don’t have the right availability gating?

3 Likes

As it happens, placeholder types cannot currently be used in default arguments at all (though unbound generics are allowed, so perhaps placeholders should be, too?), so this is only relevant right now for inlinable function bodies. Based on what @beccadax has said above it doesn't seem like the issues here are unique compared to any other new language feature and we already warn users when they attempt to build a swiftinterface that was compiled with a newer compiler than the one they're using (right?).

Is there precedent for a warning when using new language features like this?

2 Likes

Thanks everyone! The core team has accepted this proposal.

7 Likes