guard let x = x


(Jon Hull) #1

I think we should just have an “Unwrappable” protocol with a function/property that gets called by the statement. Then types can just define it for themselves based on whatever makes sense.

That, or Xiaodi’s idea for union types (since I really want union types)

Thanks,
Jon

···

> On Oct 28, 2016, at 5:55 PM, Kevin Nattinger <swift at nattinger.net <https://lists.swift.org/mailman/listinfo/swift-evolution>> wrote:
>
>>
>> On Oct 28, 2016, at 4:45 PM, Erica Sadun via swift-evolution <swift-evolution at swift.org <https://lists.swift.org/mailman/listinfo/swift-evolution> <mailto:swift-evolution at swift.org <https://lists.swift.org/mailman/listinfo/swift-evolution>>> wrote:
>>
>>
>>> On Oct 28, 2016, at 5:00 PM, Huon Wilson <huon at apple.com <https://lists.swift.org/mailman/listinfo/swift-evolution> <mailto:huon at apple.com <https://lists.swift.org/mailman/listinfo/swift-evolution>>> wrote:
>>>
>>>
>>>> On Oct 28, 2016, at 15:34, Erica Sadun via swift-evolution <swift-evolution at swift.org <https://lists.swift.org/mailman/listinfo/swift-evolution> <mailto:swift-evolution at swift.org <https://lists.swift.org/mailman/listinfo/swift-evolution>>> wrote:
>>>> <https://gist.github.com/erica/db9ce92b3d23cb20799460f603c0ae7c#detailed-design>Detailed Design
>>>>
>>>> unwrap can be used with any one-value enumeration. The unwrapped value is bound to the same symbol as the associated type.
>>>>
>>>> enum TypeName<T, U> { case anycase(T), anothercase(U) }
>>>>
>>>> // First and second are type `TypeName`
>>>> let first = TypeName.anyCase(value1)
>>>> let second = TypeName. anothercase(value2)
>>>>
>>>> guard unwrap first else { ... }
>>>> // first is now shadowed as type T
>>>>
>>>> guard unwrap second else { ... }
>>>> // second is now shadowed as type U
>>>> <https://gist.github.com/erica/db9ce92b3d23cb20799460f603c0ae7c#impact-on-existing-code>
>>> How does the compiler decide whether to succeed on anycase or succeed on anothercase respectively? In general, the compiler only statically knows that first & second are of type TypeName, not anything about which case (they could be passed in as function parameters, or returned by an opaque function e.g. `let x = OtherLibrary.returnsTypeName(); guard unwrap x else { … }`), and thus the variant to unwrap has to be chosen based only on that piece of information.
>>>
>>> It seems to me that doing this either has to be restricted to enums with an “obvious” choice for unwrapping, like Optional, or rely on a sort of forward-looking type inference that Swift doesn’t currently use to deduce the unwrapped type based on how the value is used later (and I’m not sure that works in all cases, e.g. what if T == U for the TypeName example).
>>
>> It succeeds on any one-item case and fails on any non-item case.
>
> I think he meant this:
>
> enum TypeName<T, U> { case anycase(T), anothercase(U) }
>
> func foo<T, U>(instance: TypeName<T, U>) {
> guard unwrap instance else { ... }
> }
>
> What type does instance have?

Fair enough. How about: An unwrappable enumeration must have at most one generic type so the compiler can guarantee at compile time that the type is unambiguous if the unwrapping succeeds


Introducing `Unwrappable`, a biased unwrapping protocol
(Jon Hull) #2

I wonder if Result will end up being a sub-protocol of Unwrappable (or perhaps Unwrappable will give us everything we need for Result, with Optional being an implementation which doesn’t allow an error state). Basically, in addition to everything that we talked about with Unwrappable there would be a sugared way to say “If you represent an error, throw it now”.

This way, Result basically becomes a way to record an error which can be thrown at the time (and thread) of our choosing. It would make sense to have symmetry here, so we would also want an easy way to take a throwing call/block and turn it into a Result. In other words, most things can just be designed with the current error handling model, with the throws becoming Results for the transition across thread boundaries… and being turned back into throws when you actually ‘try’ to use them.

As a throw-away syntax to illustrate the idea:

  let myResult = Result(of: try myThrowingCall()) //Any throwing call or block can become a Result

  //Later…

  try unwrap myResult //This could even be in a different thread

The reason I think we should have a protocol instead of a single generic enum/struct is that there are complex cases which could still easily fit in (or even add onto) that model if allowed.

For a concrete example, in my packrat parser, I have several different error states that can be handled differently. Some just cause it to fall back to try the next rule, while others cause it to terminate parsing or enter into a debug mode. I also have the concept of a semi-successful return with errors (i.e. It is fully parsed, but instead of a result, the parsed string is returned with a set of human-readable errors (+ locations in the string) explaining why a result could not be returned).

It would be nice for Result to be able to support that sort of thing as well (at least as an add-on), and a protocol would give us the freedom to extend the model as needed.

The reason I am bringing this up now is that it may also affect our design of Unwrappable.

Thanks,
Jon

···

On Nov 11, 2016, at 2:48 PM, Chris Lattner <clattner@apple.com> wrote:

To summarize:

1. Swift needs to introduce a `Result` type.

Yes, but this is almost certainly a Swift 5 thing. We need to settle the following arguments:

1) Does Result include async capabilities (I’m assuming we’ll get something like async await + other higher level abstractions)?

2) Do we support typed error handling at some point (so you can write “throws SpecificEnum”)?

Before we decide those, we can’t design Result.


(Erica Sadun) #3

* Just so we have a record of this discussion for future refinement, I've put this together: https://gist.github.com/erica/3daa8ec77aef2feaefca3a3a19aedee3
It addresses `Unwrappable` and `Result` but not `unwrap`. It does not really touch on the async/threading aspects of `Result` and refers to the design of `Result` as a matter for future discussion.

* My original `if case`/`guard case` is here: https://github.com/apple/swift-evolution/pull/559

* I would write up the basic `unwrap` at some point, but it seems to me that it would be heavily biased towards supporting `Unwrappable` so maybe I should hold off until the ideas behind `Unwrappable` are better hashed out.

I believe that covers all the major points of this discussion. If so, I'm going to table this until phase 2.

-- E

···

On Nov 14, 2016, at 12:24 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 11, 2016, at 2:48 PM, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> wrote:

To summarize:

1. Swift needs to introduce a `Result` type.

Yes, but this is almost certainly a Swift 5 thing. We need to settle the following arguments:

1) Does Result include async capabilities (I’m assuming we’ll get something like async await + other higher level abstractions)?

2) Do we support typed error handling at some point (so you can write “throws SpecificEnum”)?

Before we decide those, we can’t design Result.

I wonder if Result will end up being a sub-protocol of Unwrappable (or perhaps Unwrappable will give us everything we need for Result, with Optional being an implementation which doesn’t allow an error state). Basically, in addition to everything that we talked about with Unwrappable there would be a sugared way to say “If you represent an error, throw it now”.

This way, Result basically becomes a way to record an error which can be thrown at the time (and thread) of our choosing. It would make sense to have symmetry here, so we would also want an easy way to take a throwing call/block and turn it into a Result. In other words, most things can just be designed with the current error handling model, with the throws becoming Results for the transition across thread boundaries… and being turned back into throws when you actually ‘try’ to use them.

As a throw-away syntax to illustrate the idea:

  let myResult = Result(of: try myThrowingCall()) //Any throwing call or block can become a Result

  //Later…

  try unwrap myResult //This could even be in a different thread

The reason I think we should have a protocol instead of a single generic enum/struct is that there are complex cases which could still easily fit in (or even add onto) that model if allowed.

For a concrete example, in my packrat parser, I have several different error states that can be handled differently. Some just cause it to fall back to try the next rule, while others cause it to terminate parsing or enter into a debug mode. I also have the concept of a semi-successful return with errors (i.e. It is fully parsed, but instead of a result, the parsed string is returned with a set of human-readable errors (+ locations in the string) explaining why a result could not be returned).

It would be nice for Result to be able to support that sort of thing as well (at least as an add-on), and a protocol would give us the freedom to extend the model as needed.

The reason I am bringing this up now is that it may also affect our design of Unwrappable.

Thanks,
Jon


#4

I believe that covers all the major points of this discussion. If so, I'm
going to table this until phase 2.

I seem to recall another possibility that was brought up in this thread,
which is currently absent from your document…

Nevin

···

On Mon, Nov 14, 2016 at 10:41 PM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Nov 14, 2016 at 10:41 PM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

On Nov 14, 2016, at 12:24 AM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:

On Nov 11, 2016, at 2:48 PM, Chris Lattner <clattner@apple.com> wrote:

To summarize:

1. Swift needs to introduce a `Result` type.

Yes, but this is almost certainly a Swift 5 thing. We need to settle the
following arguments:

1) Does Result include async capabilities (I’m assuming we’ll get
something like async await + other higher level abstractions)?

2) Do we support typed error handling at some point (so you can write
“throws SpecificEnum”)?

Before we decide those, we can’t design Result.

I wonder if Result will end up being a sub-protocol of Unwrappable (or
perhaps Unwrappable will give us everything we need for Result, with
Optional being an implementation which doesn’t allow an error state).
Basically, in addition to everything that we talked about with Unwrappable
there would be a sugared way to say “If you represent an error, throw it
now”.

This way, Result basically becomes a way to record an error which can be
thrown at the time (and thread) of our choosing. It would make sense to
have symmetry here, so we would also want an easy way to take a throwing
call/block and turn it into a Result. In other words, most things can just
be designed with the current error handling model, with the throws becoming
Results for the transition across thread boundaries… and being turned back
into throws when you actually ‘try’ to use them.

As a throw-away syntax to illustrate the idea:

let myResult = Result(of: try myThrowingCall()) //Any throwing call or
block can become a Result

//Later…

try unwrap myResult //This could even be in a different thread

The reason I think we should have a protocol instead of a single generic
enum/struct is that there are complex cases which could still easily fit in
(or even add onto) that model if allowed.

For a concrete example, in my packrat parser, I have several different
error states that can be handled differently. Some just cause it to fall
back to try the next rule, while others cause it to terminate parsing or
enter into a debug mode. I also have the concept of a semi-successful
return with errors (i.e. It is fully parsed, but instead of a result, the
parsed string is returned with a set of human-readable errors (+ locations
in the string) explaining why a result could not be returned).

It would be nice for Result to be able to support that sort of thing as
well (at least as an add-on), and a protocol would give us the freedom to
extend the model as needed.

The reason I am bringing this up now is that it may also affect our design
of Unwrappable.

Thanks,
Jon

* Just so we have a record of this discussion for future refinement, I've
put this together: https://gist.github.com/erica/
3daa8ec77aef2feaefca3a3a19aedee3
It addresses `Unwrappable` and `Result` but not `unwrap`. It does not
really touch on the async/threading aspects of `Result` and refers to the
design of `Result` as a matter for future discussion.

* My original `if case`/`guard case` is here: https://github.com/
apple/swift-evolution/pull/559

* I would write up the basic `unwrap` at some point, but it seems to me
that it would be heavily biased towards supporting `Unwrappable` so maybe I
should hold off until the ideas behind `Unwrappable` are better hashed out.

I believe that covers all the major points of this discussion. If so, I'm
going to table this until phase 2.

-- E

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