Ad hoc enums / options

From: Christopher Kornher <ckornher@me.com>
Subject: Fwd: [swift-evolution] Ad hoc enums / options
Date: May 31, 2016 at 12:25:33 PM MDT
To: Erica Sadun <erica@ericasadun.com>

Apologies for using you as a relay...

From: Charlie Monroe via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
Subject: Re: [swift-evolution] Ad hoc enums / options
Date: May 31, 2016 at 11:43:43 AM MDT
To: Charles Constant <charles@charlesism.com <mailto:charles@charlesism.com>>
Cc: Swift Evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>, Christopher Kornher <ckornher@me.com <mailto:ckornher@me.com>>
Reply-To: Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>>

I have mixed feelings about this since it may lead to redeclarations over and over of the same values instead of actually declaring an enum.

I have two suggested “improvements”

1) Make the enum String raw-representable. Name it somehow. This does not affect Erica’s original syntax.
2) Force an explicit name.

#2 does add to the length of function declarations, so it is a tradeoff. Perhaps the name could be optional, but...

#2 would improve debug representations of the value by providing a name that can be found in source code

In a full-featured metadata system, it would probably be nice to have a type for the enum to simply the handling of all enums.

#2 is more future-proof. Systems get more complex over time and one use of a type becomes many.
The enum type name (auto-generated or required, it makes no difference) would be scoped to the function’s namespace e.g. (fixing the typo) :

class MyImage {
  func scaleAndCropImage(
        image: UIImage,
    toSize size: CGSize,
    operation: ScaleCropFitFill{ .Fit | Fill} = .Fit

      ) -> UIImage {…}
}

would be equivalent to:

class MyImage {
   enum ScaleCropFitFill {
    case Fit
    case Fill
  }

  func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: ScaleCropFitFill = .Fit
  ) -> UIImage {…}
}

There are two ways that an implementation could evolve from having one use of the enum in a call to multiple uses;

1) The function is refactored into more functions internal to the original function’s namespace: module/class/struct/enum.
  In this case, it would be appropriate to leave the enum declaration in function declaration to indicate that this is the only public use of the enum.
2) More public functions are created that use the enum
  In this case, it would be appropriate to declare the enum within the same scope. Existing code would not be affected. Smart editors could provide this refactoring.

- Chris K

···

Begin forwarded message:

Begin forwarded message:

I have two suggested “improvements”

1) Make the enum String raw-representable. Name it somehow. This does not affect Erica’s original syntax.
2) Force an explicit name.

class MyImage {
  func scaleAndCropImage(
        image: UIImage,
    toSize size: CGSize,
    operation: ScaleCropFitFill{ .Fit | Fill} = .Fit

      ) -> UIImage {…}
}

would be equivalent to:

class MyImage {
   enum ScaleCropFitFill {
    case Fit
    case Fill
  }

  func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: ScaleCropFitFill = .Fit
  ) -> UIImage {…}
}

This is not the direction I'm hoping to move in.

If an enumeration is to be used in more than one place, it should be a proper enumeration. Swift already offers dependent type support. Single-point ad-hoc enumerations create better semantics for moving from if statements that test on Boolean values to switch statements that test on phrases, without creating dependencies on other types. As Tony A points out,

Having argument labels solves some of the problems that come along with boolean arguments, but "fitImage" is a great example where the false case ("not fit?") doesn't really convey enough information (or can convey misleading information).

-- E