Hello, I recently stumbled with code (Feasibility of stopping infinite nesting) that made me want to touch up a few bits of the language. These bits are about a declaration of enumerations in nested types.
Consider, for example, the case when a developer wants to declare a long complex Array of items:
//with the current state of the language, all items have to be declared in enums
enum EitherStringOrInt { case string(String), int(Int) }
Array<EitherStringOrEnum>
The problem arises when there can be many such enumerations that only needed to express the need for type separation.
So I propose the additive change to the language, which is whenever the described need arises, the developer can write:
Array<(Int|String)>; let a: (Int|String)
//or to convince you
Array<(Int|([Double]|(String|Set<SomeCustomType>)))>
//Imagine how much redundant explicit enums you would need to write
In terms of ebnf it would be something like:
short_enum_decl := '(' [some_type](infix |) ')'
Thoughts?
1 Like
Avi
2
How would the compiler know which enum to wrap the values in?
cukr
3
You can declare a single generic enum that handles any pair of types, without adding any new features to the language
enum Either<Left, Right> {
case left(Left)
case right(Right)
}
hisekaldma
(Jonathan Hise Kaldma)
4
This has been discussed previously as "anonymous unions", and a much as I would like to see that feature myself, itās on the list of commonly rejected changes.
Oh, God, what a disappointment.
.
ps It is interesting why this was rejected because the reason seems to be somewhere underground.
It is true with pairs, but the op tries to describe it to higher arity. Either wont work for [((A|B|C)|(D|E|F))] for example
Uhm, I don't follow. What do you mean exactly?
cukr
8
Does he? It looks like pairs for me
Array<(Int|([Double]|(String|Set<SomeCustomType>)))>
Oh, yes, you are right about that Either can handle any arity. My bad. 
sveinhal
(Svein Halvor Halvorsen)
10
Iāve seen that before, and read the linked-to rationale. But I feel the link glosses over the reasoning behind the rejection of this idea, other than simply outright rejecting it.
Do you happen to know more about the reasoning behind it? Is it aesthetic, complexity, priorities, some deep incompleteness issues, or whatever? Iād like to learn.
To my naive mind, the idea seems useful.
Avi
11
I believe your confusion is predicated on my own. I don't know what you're proposing, or how the syntax works.
I would like swift to have something like shorthand disjunctive union declaration when the value of it are of different type, like with Either<A, B>. So it all about sugar: instead of writing for example Array<Either<Int, Double>>, you would write Array<(Int|Double)>. Accessing elements would be done like this:
let a: Array<(Int | String)> = [2, "a", 3, "b"]
let b = a[1]
if a is String { print(a.lowercased()) }
anandabits
(Matthew Johnson)
13
Anonymous unions have been rejected, but afaik anonymous sums have not received much discussion. I think anonymous sums would be useful to consider.
let x: (String | Int) = .0(āhelloā)
switch x {
case .0(let string): ...
case .1(let int): ...
}
As with tuples, we could support labeled cases as well:
let x: (string: String | int: Int) = .string(āhelloā)
switch x {
case .string(let string): ...
case .int(let int): ...
}
// as with tuples, the positional syntax still works:
switch x {
case .0(let string): ...
case .1(let int): ...
}
I donāt think anonymous sum types along these lines would run into any of the issues that have gotten unions on the commonly rejected list. They are simply to enums what tuples are to structs instead of requiring entirely new, subtle, and potentially compile-time-killing type system capabilities. I think itās a shame that we have structural product types but not structural sum types and would love to see that change.
6 Likes
hisekaldma
(Jonathan Hise Kaldma)
14
Thatās a really interesting idea! Do you see it working without payloads too? I can imagine that would be useful when you just want to expose something as a "knob" in an API, but donāt really want to create a named type for it:
func rowsViews<T>(_ rows: [T], shading: (none | even | odd | every: Int)) -> [UIView] {
}
rowViews(rows, shading: .none)
rowViews(rows, shading: .even)
rowViews(rows, shading: .odd)
rowViews(rows, shading: .every(3))
2 Likes
anandabits
(Matthew Johnson)
15
I think that could probably be made to work. IIRC @Erica_Sadun had a pitch along these lines at one point. Iām not sure exactly what the syntax would be but the community loves to bikeshed!
One subtlety to note is that uppercase types and lowercase case names is only a convention, so in your example even and odd could be types. We would need some way to distinguish them. Perhaps something like (none: | even: | odd: | every: Int). But that feels a little bit awkward so hopefully somebody thinks of something better.
1 Like
sveinhal
(Svein Halvor Halvorsen)
16
I'm fine with required case labels.
Wouldnāt the more technically correct separator be ^ and not |, because it is more like an xor than an or operation?
anandabits
(Matthew Johnson)
18
I'll leave the bikeshedding to others. I just want language support for sum types to be on par with product types.
(off topic, but PointFree's CasePath library is something that should really be a language feature alongside KeyPath)
2 Likes
My pitch wasn't anonymous unions, it was ad-hoc enums
image: UIImage,
toSize size: CGSize,
fitImage: Bool = true
) -> UIImage {```
And here's what I want the function signature to actually look like:
image: UIImage,
toSize size: CGSize,
operation: (.Fit | .Fill) = .Fit
) -> UIImage {```
1 Like
anandabits
(Matthew Johnson)
20
Thanks for digging up the link! Ad-hoc / structural enums are what I suggested above, only with slightly different syntax than in your pitch. I'm also suggesting that they should support for associated values. I'm agnostic on the syntax, I just want to see structural sum types added to the language. 