Ad hoc enums / options


(Erica Sadun) #1

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica


(Kevin Nattinger) #2

Definitely an interesting idea, and I like it, but how would this be used from Objective C?

···

On May 31, 2016, at 9:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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


(Erica Sadun) #3

Interop is a separate question. I'm sticking mostly to pure Swift these days, or doing all my calls from Swift. In such a light, it falls under the "not available to ObjC" or "if made available to ObjcC, does not fall under the scope of introduction of this idea" umbrellas.

-- E

···

On May 31, 2016, at 10:20 AM, Kevin Nattinger <swift@nattinger.net> wrote:

Definitely an interesting idea, and I like it, but how would this be used from Objective C?


(Christopher Kornher) #4

Forwarding on behalf of Chris Kornher:

I think that this is a great idea. It would be nice to:
  1) Have a standard way to generate these values from textual or other serialized representations.
  2) A way to be able to store these vales (as preferences, for example)

A simple way to do support this is to make these into full-fledged enums:
   1) make these rawrepresentable string enums
   2) give the type a name for use when needed: e.g.
    
  scaleAndCropImage.fitImage.options_t

    or perhaps, :
  
  scaleAndCropImage_fitImage_options_t

enabling:

struct MyPreferenceStruct {
..
var fitOptions: scaleAndCropImage_fitImage_options_t
}

Thinking about this a bit more, requiring a name like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: ScaleCropFitFitFill{.Fit | .Fill} = .Fit
    ) -> UIImage {

would be cleaner.

- Chris

···

On May 31, 2016, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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


(Brent Royal-Gordon) #5

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

As I said the last time this proposal came up, I think this is great right up until the moment you need `operation` to be computed or kept in a variable. Then you need to start copying your anonymous enum type all over your source, and there's no central place to make changes. The same thing is true of tuples, but tuples are easy to structure and destructure from individual values on the spot; anonymous enums aren't really like that.

And this problem starts occurring *very* quickly. I mean, you've left `scaleAndCropImage`'s implementation out. Imagine implementing it with a couple helper functions, and you start to see this problem occurring:

  func scaleAndCrop(image: UIImage, to size: CGSize, operation: (.fit | .fill) = .fit) -> UIImage {
    let drawingRect = rect(of: size, for: operation)
    return drawnImage(image, in: drawingRect)
  }
  
  private func rect(of size: CGSize, for operation: (.fit | .fill)) -> CGRect {

Already we have one duplication; if we add .fillWidth and .fillHeight modes, we'll need to change signatures in two places.

In short, when you dig into this proposal and start thinking of use cases that are even slightly less than completely trivial, I think the case for it falls apart.

(It's also worth noting that this isn't suitable for options, because it creates an anonymous enum, not an anonymous option set.)

···

--
Brent Royal-Gordon
Architechies


(Tony Allevato) #6

Big +1. I had similar thoughts a while back when I was writing some C++ or
Java code that had boolean arguments, and I found myself hating how
non-documenting they were and I would contrive two-valued enums to make
call sites look better. Having a crisp clean syntax for this would be
fantastic and encourage people to write self-documenting APIs.

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).

···

On Tue, May 31, 2016 at 9:17 AM Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
*fitImage: Bool = true*
) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
*operation: (.Fit | .Fill) = .Fit*
) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc
enumeration-like semantics for the call. A while back, Yong hee Lee
introduced anonymous enumerations (and the possibility of anonymous option
flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in
pursuing this further as I think the second example is more readable,
appropriate, and Swifty than the first, provides better semantics, and is
more self documenting.

Thanks for your feedback,

-- Erica

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


(Charlie Monroe) #7

Most ideas discussed here lately cannot be used from ObjC.

···

On May 31, 2016, at 6:20 PM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

Definitely an interesting idea, and I like it, but how would this be used from Objective C?

On May 31, 2016, at 9:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
   image: UIImage,
   toSize size: CGSize,
   fitImage: Bool = true
   ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
   image: UIImage,
   toSize size: CGSize,
   operation: (.Fit | .Fill) = .Fit
   ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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

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


(Félix Cloutier) #8

I like the idea, but it is worth noting that unless the syntax is generalized (which I'm not necessarily against), the type of `operation` cannot be referenced. This means that you can't declare a variable of that type, unless you assign the argument to it.

Félix

···

Le 31 mai 2016 à 09:16:56, Erica Sadun via swift-evolution <swift-evolution@swift.org> a écrit :

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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


(Vladimir) #9

IMO interesting idea.. We have ad hoc structs as tuples and it seems like ad-hoc enums also could be helpful when we don't need a separate class for using in limited scope.

I also can see advantage to use such anonymous enum type in code:

var codePath : (.one | .two | .three) = .one

...

switch codePath {
   case .one : ...
   case .two : ...
   case .three : ...
}

IMO very clean and expressive code and I like the proposed syntax.
So I'd like to have such in Swift

···

On 31.05.2016 19:16, Erica Sadun via swift-evolution wrote:

func scaleAndCropImage(
  image: UIImage,
  toSize size: CGSize,
  *operation: (.Fit | .Fill) = .Fit*
  ) -> UIImage {


(Haravikk) #10

I agree the second is much nicer, and a lot clearer on what each of the options does; fitImage: true is pretty clear, but fitImage: false is not, but the ad-hoc enum is clear on both counts. That said, the questions of interoperability are a big issue for ad-hoc enums, as either they’re too strict which becomes inconvenient (a .Fit | .Fill working with one method but not another) or too relaxed to be safe. Of course, in the latter case you’re replacing a Bool, which couldn’t be more relaxed in terms of where it accepts values from.

Still, I think in this case it would be better to fully-define an enum, as it gives you total control over compatibility and reusability of the type, which you can’t really with the ad-hoc form without making it overly complex.

The main type of ad-hoc enum I want to see is a union-type like so:

  func someMethod(value:(Int | String)) { … }

This would basically be an ad-hoc enum where each case identifies one of the possible types, and the value bound as that type. This works however because there’s no ambiguity in the meaning; an (Int | String) is the same wherever you use it, whereas a general-purpose ad-hoc enum is less clear, as an other method might also take .Fit and .Fill values, but these may have a slightly different meaning.

So yeah, I like the idea in principle, but I think in practice it has too many headaches to overcome for it to be as simple as it first appears =(

···

On 31 May 2016, at 17:16, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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


(Patrick Smith) #11

Lots of people have added their 2¢ now, you probably have about 10 dollars worth, but here goes.

While it looks nice and short, I think it has a range of drawbacks:
- It’s not a real type that can participate in protocols or extensions.
- Discourages DRY coding, because some people will think they are taking the easy way out, and even copy and paste this same declaration multiple times.
- Encourages adding subtypes to functions instead of types. Swift seems primarily type-focused more than function-focused. I remember seeing someone write their solution as an extension to UIImage, which was a nice way to do it. This subtype would be best conceptually as UIImage.FitOperation (use with multiple methods!) not scaleAndCropImage.Operation (use once!)

The beauty of Swift is that you can add methods to value types. This opens up interesting, more reusable solutions such as:

enum ContentFit {
  case fit
  case fill
  
  func adjust(size contentSize: CGSize, within bounds: CGRect) -> CGRect {
    let (widthFactor, heightFactor) = (bounds.size.width / contentSize.width, bounds.size.height / contentSize.height)
    let scaleFactor: CGFloat
    switch self {
    case .fit: scaleFactor = min(widthFactor, heightFactor)
    case .fill: scaleFactor = max(widthFactor, heightFactor)
    }
    
    let adjustedSize = CGSize(width: contentSize.width * scaleFactor, height: contentSize.height * scaleFactor)
    let adjustedOrigin = CGPoint(x: (bounds.size.width - adjustedSize.width) / 2.0, y: (bounds.size.height - adjustedSize.height) / 2.0)
    return CGRect(origin: adjustedOrigin, size: adjustedSize)
  }
}

That way you break functions up into smaller chunks, while also making the types more useful in themselves.

(For context, Erica’s original post is here: http://ericasadun.com/2016/05/31/swift-rewrite-challenge/)

Patrick

···

On 1 Jun 2016, at 2:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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


(Charles Constant) #12

An enthusiastic +1 to Erica's suggestion.

While I also see the appeal of Chris Kornher's suggestion of giving the
enum a proper Type, I think Erica's syntax is more appropriate. I haven't
though through the implications of having an "anonymous enum" but since the
main use case is just to give functions a more convenient and legible way
to specify options, I think shorter is better.

I really like this proposal. It would result in a lot of Swift code, out
there in the world, being a little easier to read :slight_smile:

···

On Tue, May 31, 2016 at 9:53 AM, Christopher Kornher via swift-evolution < swift-evolution@swift.org> wrote:

*Forwarding on behalf of Chris Kornher:*

I think that this is a great idea. It would be nice to:
1) Have a standard way to generate these values from textual or other
serialized representations.
2) A way to be able to store these vales (as preferences, for example)

A simple way to do support this is to make these into full-fledged enums:
1) make these rawrepresentable string enums
2) give the type a name for use when needed: e.g.
scaleAndCropImage.fitImage.options_t or perhaps, :
scaleAndCropImage_fitImage_options_t enabling: struct MyPreferenceStruct
{ .. var fitOptions: scaleAndCropImage_fitImage_options_t }

Thinking about this a bit more, requiring a name like:

func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
*operation: ScaleCropFitFitFill{.Fit | .Fill} = .Fit*
) -> UIImage {
would be cleaner.

- Chris

On May 31, 2016, at 10:16 AM, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
*fitImage: Bool = true*
) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
*operation: (.Fit | .Fill) = .Fit*
) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc
enumeration-like semantics for the call. A while back, Yong hee Lee
introduced anonymous enumerations (and the possibility of anonymous option
flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in
pursuing this further as I think the second example is more readable,
appropriate, and Swifty than the first, provides better semantics, and is
more self documenting.

Thanks for your feedback,

-- Erica

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

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


(Erica Sadun) #13

That is a fair point. It may be reasonable to add an ObjC interop section to the proposal template.

At the same time, I'm not sure the core Swift team has made it clear how much responsibility Swift has to this area, especially when considering the language going multiplatform.

-- E

···

On May 31, 2016, at 10:24 AM, Charlie Monroe <charlie@charliemonroe.net> wrote:

Most ideas discussed here lately cannot be used from ObjC.


(Erica Sadun) #14

From the caller's point of view, the type must be inferable and exactly match a token listed in the declaration (which would also appear in Quick Help):

let _ = scaleAndCropImage(image: myImage, toSize: size, operation: .fill)

You would not be able to assign `.fill` to a variable and use that for the operation value.

-- E

···

On May 31, 2016, at 12:35 PM, Matthew Johnson <matthew@anandabits.com> wrote:

I think I'm -1 on this. It makes things easier for the implementer of the function and harder for the caller.

It's not clear whether the caller could store an argument to pass in a variable, but if they could they would need to list out all cases in the type of the variable (unless these anonymous enums have structural subtyping). This is fragile. Any time that list changes all variable declarations will have to be updated.

Functions are implemented once and usually called more many times. It's better for callers if you just write it like this:

enum FitOrFill { case fit, fill }
func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
operation: FitOrFill = .fit
) -> UIImage {

So unless these anonymous enums are structurally subtyped I think it's a bad idea. And introducing structural subtyping here seems like a pretty large hammer for this use case.


(Matthew Johnson) #15

I think I'm -1 on this. It makes things easier for the implementer of the function and harder for the caller.

It's not clear whether the caller could store an argument to pass in a variable, but if they could they would need to list out all cases in the type of the variable (unless these anonymous enums have structural subtyping). This is fragile. Any time that list changes all variable declarations will have to be updated.

Functions are implemented once and usually called more many times. It's better for callers if you just write it like this:

enum FitOrFill { case fit, fill }
func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
operation: FitOrFill = .fit
) -> UIImage {

So unless these anonymous enums are structurally subtyped I think it's a bad idea. And introducing structural subtyping here seems like a pretty large hammer for this use case.

···

Sent from my iPad

On May 31, 2016, at 12:44 PM, Tony Allevato via swift-evolution <swift-evolution@swift.org> wrote:

Big +1. I had similar thoughts a while back when I was writing some C++ or Java code that had boolean arguments, and I found myself hating how non-documenting they were and I would contrive two-valued enums to make call sites look better. Having a crisp clean syntax for this would be fantastic and encourage people to write self-documenting APIs.

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).

On Tue, May 31, 2016 at 9:17 AM Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:
Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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

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


(Leonardo Pessoa) #16

This is interesting. I don't think there is anything preventing these
to be internally translated by the compiler as a true enums with
random names (or
any-other-way-we-shall-never-be-able-to-reference-again) thus adding
the needed ObjC interop.

L

···

On 31 May 2016 at 13:24, Charlie Monroe via swift-evolution <swift-evolution@swift.org> wrote:

Most ideas discussed here lately cannot be used from ObjC.

On May 31, 2016, at 6:20 PM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

Definitely an interesting idea, and I like it, but how would this be used from Objective C?

On May 31, 2016, at 9:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
   image: UIImage,
   toSize size: CGSize,
   fitImage: Bool = true
   ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
   image: UIImage,
   toSize size: CGSize,
   operation: (.Fit | .Fill) = .Fit
   ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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

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

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


(Matthew Johnson) #17

I think I'm -1 on this. It makes things easier for the implementer of the function and harder for the caller.

It's not clear whether the caller could store an argument to pass in a variable, but if they could they would need to list out all cases in the type of the variable (unless these anonymous enums have structural subtyping). This is fragile. Any time that list changes all variable declarations will have to be updated.

Functions are implemented once and usually called more many times. It's better for callers if you just write it like this:

enum FitOrFill { case fit, fill }
func scaleAndCropImage(
image: UIImage,
toSize size: CGSize,
operation: FitOrFill = .fit
) -> UIImage {

So unless these anonymous enums are structurally subtyped I think it's a bad idea. And introducing structural subtyping here seems like a pretty large hammer for this use case.

From the caller's point of view, the type must be inferable and exactly match a token listed in the declaration (which would also appear in Quick Help):

let _ = scaleAndCropImage(image: myImage, toSize: size, operation: .fill)

You would not be able to assign `.fill` to a variable and use that for the operation value.

If you are not allowing callers to store their argument in a variable then I am 100% opposed to this. That would the first case in Swift where you *MUST* provide a literal argument when calling a function, and *CANNOT* provide a value you store in a variable (possibly something you receive as an argument from somewhere else). Why would we want to restrict the flexibility of callers in that way?

···

On May 31, 2016, at 2:04 PM, Erica Sadun <erica@ericasadun.com> wrote:

On May 31, 2016, at 12:35 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

-- E


(Erica Sadun) #18

And the obvious answer is you can have up to 255 of these babies for the anonymous enum type, and be able to pass numerical equivalents UInt8 with compile time substitution. That the ad-hoc enumeration is basically a syntactic shorthand for UInt8, with an enforced upper bound compile time check simplifies everything including switch statements.

-- E

···

On May 31, 2016, at 3:20 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

func scaleAndCropImage(
   image: UIImage,
   toSize size: CGSize,
   operation: (.Fit | .Fill) = .Fit
   ) -> UIImage {

As I said the last time this proposal came up, I think this is great right up until the moment you need `operation` to be computed or kept in a variable. Then you need to start copying your anonymous enum type all over your source, and there's no central place to make changes. The same thing is true of tuples, but tuples are easy to structure and destructure from individual values on the spot; anonymous enums aren't really like that.


(Charlie Monroe) #19

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.

···

On May 31, 2016, at 7:31 PM, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

An enthusiastic +1 to Erica's suggestion.

While I also see the appeal of Chris Kornher's suggestion of giving the enum a proper Type, I think Erica's syntax is more appropriate. I haven't though through the implications of having an "anonymous enum" but since the main use case is just to give functions a more convenient and legible way to specify options, I think shorter is better.

I really like this proposal. It would result in a lot of Swift code, out there in the world, being a little easier to read :slight_smile:

On Tue, May 31, 2016 at 9:53 AM, Christopher Kornher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Forwarding on behalf of Chris Kornher:

I think that this is a great idea. It would be nice to:
  1) Have a standard way to generate these values from textual or other serialized representations.
  2) A way to be able to store these vales (as preferences, for example)

A simple way to do support this is to make these into full-fledged enums:
   1) make these rawrepresentable string enums
   2) give the type a name for use when needed: e.g.
    
  scaleAndCropImage.fitImage.options_t

    or perhaps, :
  
  scaleAndCropImage_fitImage_options_t

enabling:

struct MyPreferenceStruct {
..
var fitOptions: scaleAndCropImage_fitImage_options_t
}

Thinking about this a bit more, requiring a name like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: ScaleCropFitFitFill{.Fit | .Fill} = .Fit
    ) -> UIImage {

would be cleaner.

- Chris

On May 31, 2016, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Here's a function signature from some code from today:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    fitImage: Bool = true
    ) -> UIImage {

And here's what I want the function signature to actually look like:

func scaleAndCropImage(
    image: UIImage,
    toSize size: CGSize,
    operation: (.Fit | .Fill) = .Fit
    ) -> UIImage {

where I don't have to establish a separate enumeration to include ad-hoc enumeration-like semantics for the call. A while back, Yong hee Lee introduced anonymous enumerations (and the possibility of anonymous option flags) but the discussion rather died.

I'm bringing it up again to see whether there is any general interest in pursuing this further as I think the second example is more readable, appropriate, and Swifty than the first, provides better semantics, and is more self documenting.

Thanks for your feedback,

-- Erica

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

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

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


(Vladimir) #20

If you are not allowing callers to store their argument in a variable then

> I am 100% opposed to this. That would the first case in Swift where you
> *MUST* provide a literal argument when calling a function, and *CANNOT*
> provide a value you store in a variable (possibly something you receive as
> an argument from somewhere else). Why would we want to restrict the
> flexibility of callers in that way?

Definitely we *must* be able to use a variable in call to function. The problem is how (in case we agreee that the proposed feature could be useful).

I'm thinking about similarity of tuples and this anonymous enums.. If you have tuple in function parameter - how would you use variable to pass it to function? You'll define a variable of the exact same tuple as required, manually, no some separate type provided for this. Yes, if tuple in function definition changed - you'll need to change tuple on caller side:

func foo(int: Int, tuple: (Int, String)) {}

foo(1, tuple: (1, "string"))

var tupleVar : (Int, String) = (1, "string")

foo(1, tuple: tupleVar)

So, why not have the same for such anonymous enums?

func foo(int: Int, variant: (.one | .two)) {}

foo(1, variant: .one)

var enumVar : (.one | .two) = .one

foo(1, variant: enumVar)

Seems like consistent solution.

···

On 31.05.2016 22:07, Matthew Johnson via swift-evolution wrote:

On May 31, 2016, at 2:04 PM, Erica Sadun <erica@ericasadun.com >> <mailto:erica@ericasadun.com>> wrote:

On May 31, 2016, at 12:35 PM, Matthew Johnson <matthew@anandabits.com >>> <mailto:matthew@anandabits.com>> wrote:

I think I'm -1 on this. It makes things easier for the implementer of
the function and harder for the caller.

It's not clear whether the caller could store an argument to pass in a
variable, but if they could they would need to list out all cases in the
type of the variable (unless these anonymous enums have structural
subtyping). This is fragile. Any time that list changes all variable
declarations will have to be updated.

Functions are implemented once and usually called more many times. It's
better for callers if you just write it like this:

enum FitOrFill { case fit, fill }
func scaleAndCropImage(
  image: UIImage,
  toSize size: CGSize,
  *operation: FitOrFill = .fit*
  ) -> UIImage {

So unless these anonymous enums are structurally subtyped I think it's a
bad idea. And introducing structural subtyping here seems like a pretty
large hammer for this use case.

From the caller's point of view, the type must be inferable and exactly
match a token listed in the declaration (which would also appear in Quick
Help):

let _ = scaleAndCropImage(image: myImage, toSize: size, operation: .fill)

You would not be able to assign `.fill` to a variable and use that for
the operation value.

If you are not allowing callers to store their argument in a variable then
I am 100% opposed to this. That would the first case in Swift where you
*MUST* provide a literal argument when calling a function, and *CANNOT*
provide a value you store in a variable (possibly something you receive as
an argument from somewhere else). Why would we want to restrict the
flexibility of callers in that way?

-- E

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