[swift-evolution] [Pitch] Use enums as enum underlying types

Dennis Lysenko dennis.s.lysenko at gmail.com
Sat Dec 19 15:58:30 CST 2015


Felix,

I cannot speak for anyone but myself, but my idea was that the "included"
cases would be treated as a member--whether first or second class, still a
member--of the enum including them. I wholeheartedly agree that the example
you wrote up would be undesirable.

On Sat, Dec 19, 2015, 4:36 PM Félix Cloutier <felixcca at yahoo.ca> wrote:

> I'm biased as the pitcher, but I find that an "inheritance" model would be
> more straightforward than an inclusive model.
>
> If I understand you correctly, with this model:
>
> enum NetworkException {
>   case NoInternetError, SecurityError
> }
>
> enum ChangePictureError {
>   case NetworkException(NetworkException)
>   case ParseException(ParseException)
>   case PictureTooLarge
> }
>
>
> you're saying that we should be able to write:
>
> let x: ChangePictureError = NetworkException.NoInternetError
>
>
> The implicit conversion from NetworkException to ChangePictureError
> reminds me of C++ implicit constructors, which are generally frowned upon,
> so I'm not sure that this is the best way forward.
>
> On the other hand, going back to my original example:
>
> enum MyLibError: ErrorType {
> case FileNotFound
> case UnexpectedEOF
> case PermissionDenied
> // ... 300 cases later
> case FluxCapacitorFailure
> case SplineReticulationError
> }
>
> enum FileSystemError: MyLibError {
> case FileNotFound = .FileNotFound
> case UnexpectedEOF = .UnexpectedEOF
> case PermissionDenied = .PermissionDenied
> }
>
>
> I can easily rationalize that FileSystemError is implicitly convertible to
> MyLibError because of the "inheritance" relationship.
>
> Félix
>
>
> Le 19 déc. 2015 à 14:28:44, Matthew Johnson <matthew at anandabits.com> a
> écrit :
>
>
> On Dec 18, 2015, at 11:34 AM, Dennis Lysenko via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Sorry, I got a bit too excited and skimmed over the most important part of
> the idea. So this is a special type of enum declaration in which you cannot
> declare any new enum members. I personally have not seen a use for this in
> my code but I would love to hear others' response to it. It is a very
> interesting idea though.
>
> I'm going to go out on a limb with an idea that is in the same vein as
> this one: What if we favored composition over inheritance here, and made it
> so that you could transparently refer to members of other enums *without*
> having another enum as a backing type?
>
> e.g., you have:
> enum NetworkException {
>   case NoInternetError, SecurityError
> }
>
> enum ParseException {
>   case FailedResponse(statusCode: Int)
>   case EmptyResponse
>   case MissingField(fieldName: String)
> }
>
> As two general classes of errors. But for a full API call wrapper, you
> might want an error class that composes the two, so that when calling the
> API call from your UI code, you can display a "please check your
> connection" message for NoInternetError, a "Please log in" error for
> FailedResponse with statusCode=401, or a "server error" message for any of
> the rest.
>
> I wonder how do you and others feel about that use-case? I have certainly
> seen it come up a lot in real-world projects that require resilient UI
> interactions with nontrivial networking operations.
>
> Here are some quick code samples off the top of my head for how we might
> go about this (let's say the API operation is "change profile picture":
>
> enum ChangePictureError {
>   include NetworkException
>   include ParseException
>   case PictureTooLarge
> }
>
>
> By including all of the cases you make it possible for ChangePictureError
> to be a supertype of NetworkException and ParseException.  This is a pretty
> interesting idea.  It might be worth exploring.
>
> One thing that would need to be considered is that ideally if the actual
> values was a NetworkException case you would want to be able to call any
> methods exposed by Network Exception.  A good way to accomplish that might
> be to add implicit conversion as well as syntactic sugar for nested enums.
> So if we have this:
>
> enum ChangePictureError {
>   case NetworkException(NetworkException)
>   case ParseException(ParseException)
>   case PictureTooLarge
> }
>
> I can do this:
>
> var error: ChangePictureError // set somewhere, can be set with a
> NetworkException or a PictureTooLarge
> switch error {
> case .NoNetworkError:                           // equivaluent to case
> .NetworkException(.NoNetworkError)
> case .NoInternetError:                            // equivaluent to case
> .NetworkException(. NoInternetError)
> case .FailedResponse(let statusCode): // equivaluent to case
> .ParseException(.FailedResponse(let statusCode))
> case .EmptyResponse:                          // equivaluent to case
> .ParseException(.EmptyResponse)
> case .MissingField(let fieldName):         // equivaluent to case
> .ParseException(. MissingField(let fieldName))
> case .PictureTooLarge:
> }
>
> The syntactic sugar would only work for case names where there is no
> overlap.  Case names that overlap would need to be explicitly
> disambiguated.  The syntactic sugar and implicit conversions could allow
> for either single-level nesting or arbitrary nesting depth.  An example of
> arbitrary depth might be ParseException also containing a ValidationError
> case:
>
> enum ValidationError {
>   case OutOfRange
>   case InvalidType
> }
>
> enum ParseException {
>   case ValidationError(ValidationError)
>   case FailedResponse(statusCode: Int)
>   case EmptyResponse
>   case MissingField(fieldName: String)
> }
>
> Mostly just thinking out loud here and exploring the idea.  What do others
> think of this?
>
>
> or
>
> enum ChangePictureError {
>   compose NetworkException.NoInternetError
>   compose ParseException.EmptyResponse
>   compose ParseException.FailedResponse(statusCode: Int)
>   case PictureTooLarge
> }
>
> Not a proposal by any stretch of the imagination, just a potential
> direction inspired by your idea, Felix.
>
>
> On Fri, Dec 18, 2015 at 12:21 PM Dennis Lysenko <
> dennis.s.lysenko at gmail.com> wrote:
>
>> Felix,
>>
>> This seems to be very interestingly tied into your comments about
>> polymorphism in 'throws' type annotations. Would you not feel that allowing
>> enums to be built on top of other enums would promote the kind of egregious
>> proliferation of exception polymorphism that discourages so many from
>> following Java's checked exception model?
>>
>> On Fri, Dec 18, 2015 at 11:29 AM Félix Cloutier <
>> swift-evolution at swift.org> wrote:
>>
>>> Hi all,
>>>
>>> Swift currently has more or less three conceptual types of enums:
>>> discriminated unions, lists of unique tokens, and lists of value of a raw
>>> type.
>>>
>>> > // Discriminated unions
>>> > enum Foo {
>>> >       case Bar(Int)
>>> >       case Baz(String)
>>> > }
>>> >
>>> > // Lists of unique tokens (mixable with discriminated unions)
>>> > enum Foo {
>>> >       case Frob
>>> >       case Nicate
>>> > }
>>> >
>>> > // Lists of raw values
>>> > enum Foo: String {
>>> >       case Bar = "Bar"
>>> >       case Baz = "Baz"
>>> > }
>>>
>>> I think that the last case could be made more interesting if you could
>>> use more types as underlying types. For instance, it could probably be
>>> extended to support another enum as the backing type. One possible use case
>>> would be to have a big fat enum for all the possible errors that your
>>> program/library can throw, but refine that list into a shorter enum for
>>> functions that don't need it all.
>>>
>>> > enum MyLibError: ErrorType {
>>> >       case FileNotFound
>>> >       case UnexpectedEOF
>>> >       case PermissionDenied
>>> >       // ... 300 cases later
>>> >       case FluxCapacitorFailure
>>> >       case SplineReticulationError
>>> > }
>>> >
>>> > enum FileSystemError: MyLibError {
>>> >       case FileNotFound = .FileNotFound
>>> >       case UnexpectedEOF = .UnexpectedEOF
>>> >       case PermissionDenied = .PermissionDenied
>>> > }
>>>
>>> This example could be made simpler if the `= .Foo` part was inferred
>>> from the name, but you get the idea.
>>>
>>> In this case, it would be helpful (but not required) that
>>> FileSystemError was convertible into a MyLibError, so that it could be
>>> transparently rethrown in a function that uses the larger enum. I
>>> personally don't see why enums with a specified underlying type can't be
>>> implicitly converted to it, but this is not currently the case and it
>>> probably deserves some discussion as well.
>>>
>>> Is there any interest in that?
>>>
>>> Félix
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>  _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151219/79b6978e/attachment.html>


More information about the swift-evolution mailing list