Opaque result types

(Mox) #244

If the goal is to reduce the amount of characters to write, then there's obvious choice here. But if you want someone unfamiliar with the code to understand what the code does, I think the Any<...> approach or something like that goes a long way to make things easier (and it also reads better as a sentence).

Now, I don't mind having shortcuts or shorthands for power users. What I wouldn't want to see is shorthand being the only way to do it.

(Joe Groff) #245

We could do like recent versions of Rust do, and make it so that the existential type requires a modifier (which could be Any) before the constraint:

struct Thing {
  var data: Any Collection<Element == Int>

which would mirror how Doug suggests using opaque for the hidden concrete type case.

(Dave Abrahams) #246

I read your comments. I think you're missing my point, which is not earth shattering: we could save a keyword if we eventually want to introduce newtype because if it is used in the position where we're using opaque currently, it can be thought of as an “anonymous newtype”. You'd still have anonymous newtype typealiases that act just like the proposed typealias X = opaque … construction.

Though now that I type it, the name “anonymous newtype typealias” seems to suggest the wrong thing…
…so maybe this idea has value < 0. :stuck_out_tongue_winking_eye:

(Joe Groff) #247

In turn, I think you missed Doug's point—the opaque result type cannot be a newtype in the general case, because that would require forwarding of the exposed conformances from the underlying concrete type to the newtype, which is not always possible to automate if there are recursive associated types or Self requirements in structural positions.

(Dave Abrahams) #248

I suppose that's true if you think an anonymous newtype has to work the same way as a nominal one. Maybe this violates the expected meaning of newtype too much to tolerate, but my idea was that a typealias for an anonymous newtype would act exactly the way a typealias for an opaque type is proposed to act.

(Michael J LeHew Jr) #249

Been following along for some time now, and wanted to offer a super straw-man idea for improving the syntax.

Would something like this work?

func dance<A, B>() -> 
  opaque Foo<
    AssocType1 == A, 
    AssocType2 == Result<B, Swift.Error>> // assuming the Boogaloo happens :slight_smile: 
    A: Equatable
    B: Equatable

It retains the conditional where-clause flexibility (at the cost of extra generics admittedly), but also provides a bit of 'scope' to the relevant parts of the opaque type itself.

Also, is there ever a reason that you'd want to say, express opaque type of opaque types? That sort of context is not really distinguishable in the _.assocType flavor. It seems like good information to have/not lose perhaps.

For instance, is this even meaningful to want?

func randomMatrix() -> opaque Collection<opaque Collection<Element: Double>>

Certainly such an idea is expressible unambiguously with a type erasers.

(Joe Groff) #250

I have a minimal prototype implementation of opaque result types in this pull request:

Limitations include:

  • The opaque type is spelled __opaque Protocol or __opaque ProtocolA & ProtocolB [& ProtocolC...] as placeholder spelling.
  • Opaque types can only appear as the return type of func declarations, not properties or closures.
  • where clause constraints on the opaque type are not yet supported, nor are conditional conformances on the opaque type. However, the protocols in the __opaque type can be arbitrary protocols, with any number of associated type or Self type requirements. __opaque types can in turn be inferred as associated types of protocol conformances.
  • The runtime and resilience aspects are not yet implemented. Changing the underlying type of a public opaque type will break ABI.

Some known issues:

  • Because of the missing runtime support, I substitute out opaque types during SILGen in a rather hacky unprincipled way. It's likely that the optimizer, particularly generic specialization, may still cause crashes.
  • The type checker currently crashes if there's a type error in the underlying type, such as if it does not conform to the required protocols.
  • Diagnostics involving opaque types will print the type with a compiler-internal representation involving the original decl name and generic arguments, instead of anything human-understandable.

I asked CI to build a toolchain: https://ci.swift.org/job/swift-PR-toolchain-osx/170//artifact/branch-master/swift-PR-21137-170-osx.tar.gz I'd appreciate help kicking the tires on this. Thanks!


Should it be conveyed to the programmer that the opaque value has value/reference semantic?
It seems like something programmer will want to know, (or else we would restrict its assignment to multiple location, implying unique semantic, which is irrelevant here, and is a different beast entirely).

So the question is, should it be conveyed to the programmer that the opaque value has value/reference semantic?

(Matthew Johnson) #252

There is nothing about opaque result types that make them unique in this respect from any other generic or existential context where the concrete type is unknown. Right now, AnyObject can be used to consorting to reference semantics. Perhaps in the future we will be able to use AnyValue to constrain to value semantics (see Strict Value Semantics).