representation of enum member values in swift? - swift

enum CompassPoint {
case north, east, south, west
}
var compassHeading = CompassPoint.west
I've read that "The case values of an enumeration are actual values, not just another way of writing their raw values." I'm confused about this statement. If cases are new types themselves, then shouldn't it be initialized as:
var compassHeading = CompassPoint.west()
According to apple, enums don't include implicit initializers.. which confuses me even more.

TLDR;
If typing out a case of an enum where the case has associated values, then the type of this case will be a higher order function with arguments matching the associated values and return type matching the enum itself. Since different cases are allowed to have different types (and number) of associated values, naturally the type of a typed out enum case can differ. The importance here is to differ between an enum instance (which always have a given case as value) and the explicit types of the cases themselves (when not values).
An enum instance is always associated with a case, and always have the type of the enum
I don't know the source of your quotations, but the Language Guide is quite straightforward on describing each case as value:
An enumeration defines a common type for a group of related values and
enables you to work with those values in a type-safe way within your
code.
The typed cases of enum:s with cases of different associated values will define different types of higher order function types, all returning the type of the enum
But in addition the value-view of the case instance of an enum, it might be noted that each case (typed out!) itself has a type (although not a "new" type in the sense that struct Foo {} would), which may differ between different cases of the same enum. If the cases have no associated value(s), this type simply equals the enum itself, but if the case use associated values, then the type of the case will be a higher order function type with arguments typed as the associated values and return type being the enum type itself. Since different cases can have different associated values, it naturally follows that different cases can correspond to different types.
Alternatively, enumeration cases can specify associated values of any
type to be stored along with each different case value, much as unions
or variants do in other languages. You can define a common set of
related cases as part of one enumeration, each of which has a
different set of values of appropriate types associated with it.
...
You can define Swift enumerations to store associated values of
any given type, and the value types can be different for each case of
the enumeration if needed.
enum Foo {
case bar
case baz(Int) // Int associated value
case bax() // Void associated value
}
print(type(of: Foo.bar)) // Foo
print(type(of: Foo.baz)) // (Int) -> Foo
print(type(of: Foo.bax)) // () -> Foo
func foo(_ closure: (Int) -> Foo) -> Foo {
return closure(42)
}
let foobaz = foo(Foo.baz) // 'foobar' is 'Foo.baz' (ass. value 42)
let foobar = foo(Foo.bar) // wont compile, type mismatch
let foobax = foo(Foo.bax) // wont compile, type mismatch
Now, since different cases have different types (when typed out, not when part of a Foo instance), "initialization" of given case to a given enum instance will look different depending on whether the case have any associated values or not.
enum Foo {
case bar
case baz(Int) // Int associated value
case bax() // Void associated value
}
var foo = Foo.bar // no associated values
foo = Foo.baz(42) // expects Int associated value: needs to be _invoked_(/called)
foo = Foo.bax() // expects empty tuple '()' associated value: needs to be _invoked_(/called)
As you can see, a case instance without an associated value is instantiated simply by typing out the enum type and the case (since the type of this case will be the enum itself: compare with Foo.bar), whereas cases with associated values (even ()) will need to be invoked when instantiated. This invokation, particularly for the bax() case above, might look a lot like some implicit initialization, but its simply the invokation of a closure type to receive an instance of the return type Foo.
let aVoidFooClosure = Foo.bax
let aFooInstance = aVoidFooClosure()
// invoke! This now have value 'Foo.bax' (empty tuple '()' associated value)

If cases are new types themselves, then shouldn't it be initialized as:
var compassHeading = CompassPoint.west()
It's just a matter of notation. But I think you can justify it to yourself like this.
It is possible to add an initializer of the kind you seem to expect, but then what you are initializing is CompassWest, not a particular case:
enum CompassPoint {
case west
init() {
self = .west
}
}
let cp = CompassPoint()
Moreover, if CompassPoint's west case had an associated value, what you ask for is just the sort of thing you would say:
enum CompassPoint {
case west(Int)
}
let cp = CompassPoint.west(25)
But since no case has an associated value, all CompassPoint.west objects are effectively the same object. No state is maintained — remember, an enum has no stored properties. So this is not like instantiating a struct or a class. CompassPoint.west is not an instantiation of this case of CompassPoint; it the one and only CompassPoint.west in the universe.
(In that sense, the case .west is more like a static property of a struct than like an instance of a class. And so the notation for referring to it is just like the notation for referring to a static property of a struct.)

Related

Overcoming Type alias '…' references itself

Background
I have an enum which, simplified, goes like this:
enum Container<T> {
case a(T)
case b(T)
case c
}
I want to be able to instantiate this with a few different types for the generic and use typealias for that:
typealias IntContainer = Container<Int>
typealias FloatContainer = Container<Float>
Problem
So far, this is all fine. However, I also want to create a recursive instance of this:
typealias CyclicContainer = Container<CyclicContainer>
Swift reports this with a compile error:
Type alias 'CyclicContainer' references itself
… This error is still reported when I change the Container declaration to:
indirect enum Container<T>
This is a bit annoying, because this would be wholesome and compiling Swift:
indirect enum CyclicContainer {
case a(CyclicContainer)
case b(CyclicContainer)
case c
}
Workaround 1
There is a work around for this. I can declare, for example:
indirect enum CyclicContainer {
case container(Container<CyclicContainer>)
}
… However, this becomes awkward for concise descriptions of a CyclicContainer instance:
let cyclicContainer: CyclicContainer = .container(.a(.container(.b(.container(.c)))))
Instead of just:
let cyclicContainer: CyclicContainer = .a(.b(.c))
Workaround 2
I could also simply create a separate enum for the recursive case, as shown earlier:
indirect enum CyclicContainer {
case a(CyclicContainer)
case b(CyclicContainer)
case c
}
However, I'll need to: create functions to convert between CyclicContainer and Container; re-implement various member functions that exist on Container<T>; and keep the two types in sync in future if I add a new case or change a case name.
This isn't terribly arduous and it's the approach I'm leaning towards. But it seems a shame to have to do this when Swift can handle an indirect enum completely happily, but not when its induced by instantiation of a generic argument on the enum.
Question
Is there a better way?

Swift enum cannot assign default raw value

Sample code:
enum BestLetters {
case A
case B
case C
case Default = B
}
The error in the Default case: Raw value for enum case must be a literal
My understanding is that enum raw values are limited to certain types (Ints, Strings, Booleans, etc). Obviously BestLetters isn't one of those types and the compiler is complaining.
How can I set the Default type's raw value to one of the other "letters"? If gives any context, the reason for this odd syntax is that I am trying to imitate something I saw in obj-c.
Thank you for your help.
In (Objective-)C an enumeration defines a set of named integer constants, and those need not be distinct:
enum {
A = 1,
B = 2,
C = 99,
Default = B
};
The cases of a Swift enum represent mutually distinct values, and their raw values – if assigned – must be unique:
enum BestLetters: Int {
case a = 1
case b
case c
case `default` = 2 // Error: Raw value for enum case is not unique
}
On the other hand, enumerations in Swift are first-class types, so that you can define a static property for that purpose:
enum BestLetters {
case a
case b
case c
static let `default` = BestLetters.b
}
(This is also how Swift imports C enumerations with duplicate values, compare touchIDLockout deprecated in iOS 11.0.)

How does this code to generate enum work in Swift? [duplicate]

In Swift, there is Raw Value in Enumeration and Default Value in class and structure. What's the different? Can someone explain that for me?
Ex. of Raw Values of Enumeration (From the Office Swift Document)
enum ASCIIControlCaracter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}
From Apple docs:
Raw Values
The barcode example in Associated Values shows how cases of an
enumeration can declare that they store associated values of different
types. As an alternative to associated values, enumeration cases can
come prepopulated with default values (called raw values), which are
all of the same type.
So I guess it is the same.
On the other hand, with "default value", you may be referring to the default value of an enum case where no values have been set, for example:
enum TestEnum: Int {
case A
case B
}
Here, TestEnum.A has a default value of 0, and TestEnum.B has a default value of 1.
Raw value refers to the actual value of an enum case (in the enum's type, in this example it would be Int):
enum TestEnum: Int {
case A
case B = 3
}
Here, TestEnum.A has the default value (which is also the raw value) of 0, and TestEnum.B has a raw value of 3 (which is no longer the default value).

Swift: Recursive use of generic struct

I'm playing around with linked lists in Swift and I'm using:
struct Node<Element> {
var next: Node<Element>?
}
it yields this error:
Recursive value type "Node<Element>" is not allowed.
I tried using indirect on both the struct declaration and the property but neither worked. How do you implement this type of structure?
When you write var next: Node<Element>?, Swift tries to put this storage inline, but since it's recursive, that results in an infinite-size struct.
indirect only applies to enums, so you could do something like this:
enum Node<Element> {
indirect case Node(Element, next: Node?)
}
Node.Node(42, next: nil)
Or you can use a regular reference type, a.k.a. class:
class Node<Element> {
var next: Node<Element>?
}
It seems that is not possible to create a recursive use of struct, but you can do by using enum, associated values and the indirect attribute.
A recursive enumeration is an enumeration that has another instance of
the enumeration as the associated value for one or more of the
enumeration cases. You indicate that an enumeration case is recursive
by writing indirect before it, which tells the compiler to insert the
necessary layer of indirection.
indirect enum Tree<Element: Comparable> {
case Empty
case Node(Tree<Element>,Element,Tree<Element>)
}
Code taken from AirspeedVelocity.
It's not possible with Structs. It's also doesn't seem wise to do linked list with value types. Unless you feel like doing
head.next.next.next.next.next = head.next.next.next.next.next.next
to delete the seventh node, you're going to want to be able to set
let next = head.next

Enum case with associated value of generic type with constraints

Is there a way, using generics and type constraints, to collapse the first two cases of this enum into one?
enum AllowedValueRange {
// Associated value represents (min, max). 'nil' represents
// "no limit" for that interval end (+/- infinity)
case IntegerRange(Int?, Int?)
// Associated value represents (min, max). 'nil' represents
// "no limit" for that interval end (+/- infinity)
case FloatRange(Float?, Float?)
// A finite set of specific values of any type
case ArbitrarySet([Any])
// (...Other cases with different structure of associated values
// or no associated values...)
}
Addendum:
I know I can specify a generic type for the whole enum, but only these two types need one. Also, I think it needs to conform to both Equatable and Comparable, but I can't quite find the syntax to specify that...
EDIT: It turns out Comparable contains Equatable (?), so perhaps I can do this:
enum AllowedValueRange {
// Associated value represents (min, max). 'nil' represents
// "no limit" for that interval end (+/- infinity)
case NumericRange((min:Comparable?, max:Comparable?))
// (rest omitted)
(Also switched pair of associated values with single, named tuple of two values)
You could define
enum AllowedValueRange {
case NumericRange((min:Comparable?, max:Comparable?))
}
but that would allow you to instantiate a value with two
unrelated comparable types, for example
let range = AllowedValueRange.NumericRange((min: 12, max: "foo"))
which is probably not what you want. So you need a generic type
(which is restricted to be comparable):
enum AllowedValueRange<T: Comparable> {
case NumericRange((min:T?, max:T?))
}
Option 1:
You can a static function with the desired generics. For example:
static func numeric<N: Comparable>(range: (min: N?, max: N?)) -> AllowedValueRange {
.numericRange(range)
}
You can also add #available to warn the caller about the direct usage and suggest an auto-fix:
#available(swift, deprecated: 1.0, renamed: "numeric(range:)")
case numericRange((min: Any?, max: Any?))
Note:
It may be weird for this use-case but it may make more sense for different situations like passing ViewBuilders to AnyView cases 🤷🏻‍♂️
Option 2:
You can also wrap allowed types inside another enum (as a namespace) and use it like:
enum ValueRange {
static func numeric<N: Numeric>(range: (min: N?, max: N?)) -> AllowedValueRange {
switch range {
case let (min, max) as (Int?, Int?): return .IntegerRange(min, max)
case let (min, max) as (Float?, Float?): return .FloatRange(min, max)
default: fatalError("Unknown type")
}
}
}
so the caller will see only 1 method and that takes generic Numerics:
Note:
The <<error type>> Will go and will be detected by the compiler as soon as you pass in the parameters.
Conclusion:
Each of these methods (including another answer by #Martin R) has its own cons and pros and you should pick the one which is right for your needs