Problem with recursive enums in Swift 5.1 - swift

I'm learning recursive enums in Swift 5.1 with Swift documentation.
Here is a code.
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
print(ArithmeticExpression.evaluate(product))
I think something is going wrong in the last line of code.
What does that mean?

evaluate(_:) is an instance function of ArithmeticExpression, i.e. you have to call it on an instance of ArithmeticExpression (with that instance being what self refers to). The type of evaluate(_:) is (ArithmeticExpression) -> Int.
Swift lets you call instance functions on types. What you get back is an unbound instance function. That is to say, a function with no value bound as its self value yet. That's what you're doing when you run ArithmeticExpression.evaluate on its own. The unbound instance function that you get back has the type:
(ArithmeticExpression) -> (ArithmetricExpression) -> Int
// ^--- the "self" ^--- the "expression" param ^--- the final return value.
By calling it and providing product as an argument (ArithmeticExpression.evaluate(product)), what you get back is a function of type (ArithmeticExpression) -> Int. This function is a bound instance function, i.e. self is now bound (it now has the value of product), but it's awaiting to be called yet again, with another ArithmeticExpression as an argument.
There's two ways to solve this to achieve what you want:
Either make this a static function. A static function isn't called on an instance, it's called directly on the type, as you tried to do:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
// Make it static here
static func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
print(ArithmeticExpression.evaluate(product))
Keep evaluate as an instance function, but call it directly on the instance your want to evaluate, rather than on the type. Since self would be the expression you're interested in, you no longer need the expression parameter:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
func evaluate() -> Int {
switch self {
case let .number(value):
return value
case let .addition(left, right):
return left.evaluate() + right.evaluate()
case let .multiplication(left, right):
return left.evaluate() * right.evaluate()
}
}
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
print(product.evaluate())
I would say this is probably the more "idiomatic" version.

Related

How can I select the correct Int type at runtime

I am trying to determine the correct Int type to use at runtime.
Essentially, I am trying to do something like this:
func sizeToType<T>(size:UInt8) throws -> T.Type {
switch size {
case 1:
return UInt8.self
case 2:
return UInt16.self
case 4:
return UInt32.self
case 8:
return UInt64.self
default:
throw MyError.runtimeError("Incorrect size")
}
But this won't compile, and force me to write it this way:
func UTILtoType<T : FixedWidthInteger>(size:UInt8) throws -> T.Type {
switch size {
case 1:
return UInt8.self as! T.Type
case 2:
return UInt16.self as! T.Type
case 4:
return UInt32.self as! T.Type
case 8:
return UInt64.self as! T.Type
default:
throw MyError.runtimeError("Incorrect size")
}
But I am worried that the latter will actually not work as I expect. I don't want my UInt8 to be "upcasted" to an Int.
What's the right way to do this?
EDIT: to answer the question of what I am trying to accomplish. My end goal is to have a function that might look like this (I am trying to make this work, I know this does not compile as is):
static func parseInt<T : FixedWidthInteger> (unsafeRawPointer:UnsafeRawPointer, stride: Int, from:Int, length:Int) -> T {
var res : UTILtoType(size:length) = 0
for x in 0..<length {
res += (unsafeRawPointer.advanced(by:(from + x) * stride).load(as:UTILtoType(size:length).self))
}
return res
}
so that I can end up having the ability to simply call:
let a = parseInt(unsafeRawPointer:pointer, stride:1, from:0, len:2)
and this will create a UInt16 and assign it to a, which will be of type UInt16. It seems redundant to me to specify both the length and UInt16, so I was hoping I could infer the type.

Dynamically initialize enum based on associated value

This is my enum:
enum E {
case a(Int), b(String)
}
The enum's associated value types are unique and always exactly one.
Let's say I have this variable:
let myInt = 0
I want to create an instance of E, based on variable myInt, dynamically. This should result in:
E.a(0)
But in the 'real world', I don't know what property I get. I only know one thing: I can initialize enum E with it. I need to dynamically initialize the enum, based on a property value. I currently have a huge switch on the property to initialize the enum, I don't want that.
But I have no idea how to accomplish this task. I tried mirroring the enum type, but I get a complex type and I have no idea how to proceed initializing it even if I know the types.
So I get a property of a certain type. I know that certain type matches a case in enum E, because there is exactly one case which associated value corresponds to the property type. I want to initialize an instance of that enum with that case, with the value of the property.
If your only starting point is the type of what will eventually be an associated value, you can use a switch statement:
enum E {
case a(Int)
case b(String)
init(associatedValue: Any) {
switch associatedValue {
case is Int:
self = .a(associatedValue as! Int)
case is String:
self = .b(associatedValue as! String)
default:
fatalError("Unrecognized type!")
}
}
}
let response = E(associatedValue: 1) // .a(1)
let other = E(associatedValue: "haha!") // .b("haha!")
The problem here is this switch must be exhaustive, meaning cover all types. So you either need a dumping ground case (.unreachable(Any)) or a fatalError so you can catch these in development.
You can use a custom initializer: (I have used more descriptive names)
enum TypeFinder {
case int(Int)
case string(String)
case unknown(Any)
init(value: Any) {
switch value {
case let v as Int: self = .int(v)
case let v as String: self = .string(v)
default: self = .unknown(value)
}
}
}
Testing:
var unknownTypeValue: Any = "Testing.."
print(TypeFinder(value: unknownTypeValue))
unknownTypeValue = 1234
print(TypeFinder(value: unknownTypeValue))
unknownTypeValue = true
print(TypeFinder(value: unknownTypeValue))
I believe you can do something like that
enum E: ExpressibleByStringLiteral, ExpressibleByIntegerLiteral {
case a(Int), b(String)
init(stringLiteral value: StringLiteralType) {
self = .b(value)
}
init(integerLiteral value: IntegerLiteralType) {
self = .a(value)
}
}

Creating an enum instance

suppose I have
enum Example {
case one(string: String)
case two(string: String)
}
and now I have
let x = Example.one(string: "Hello")
The question:
let y = ?
how do I create another instance of the same enum in e, so that I end up with y == .one("World"))
The types of enum cases with associated values are closures with arguments corresponding to the type of the associated values, and with a return corresponding to the type of the enum (with the value of the return being the specific case). I.e., for your example above, the type of Example.one as well as Example.two is (String) -> Example, where the closures expressed by these two cases yield different results; instances of .one(...) and .two(...), respectively.
Hence, instead of writing your own method to "clone" a given case, you could simply have a computed property which returns the already existing closures Example.one and Example.two (if self is one or two, respectively), which can subsequently be invoked upon a String argument to construct a new Example instance (with value .one or .two; along with the supplied associated String value).
E.g.:
enum Example {
case one(string: String) // type: (String) -> Example
case two(string: String) // type: (String) -> Example
var caseClosure: (String) -> Example {
switch self {
case .one: return Example.one
case .two: return Example.two
}
}
}
let x = Example.one(string: "Hello") // .one("Hello")
let y = x.caseClosure("World") // .one("World")
However, since all the cases in your example are closures of the same type, namely (String) -> Example (i.e. have the same number and type(s) of associated values), you might as well, as already proposed in a comment by #Hamish, wrap an enum with no associated values in a struct along with the always-String "associated value" a separate member of the struct. E.g. expanding Hamish's example with some initializers:
struct S {
enum E {
case one
case two
}
var e: E
var string: String // Since "associated value" is always the same type
init(_ e: E, string: String) {
self.e = e
self.string = string
}
init(from s: S, string: String) {
self.e = s.e
self.string = string
}
}
let x = S(.one, string: "Hello")
let y = S(from: x, string: "World")
let z = S(x.e, string: "World")
You do that by calling the Initializer exactly like you did for x:
enum Example {
case one(string: String)
case two(string: String)
}
let x = Example.one(string: "Hello")
print(x) // Prints one("Hello")
let y = Example.one(string: "World")
print(y) // Prints one("World")
Also, The , in your enum declaration is wrong and has to be removed.
UPDATE:
The comment explained the question in more detail, so here is my updated answer:
An elegant way to solve this is to use a function on the original enum type Example.
enum Example {
case one(string: String)
case two(string: String)
func cloneWith(string: String) -> Example {
switch self {
case .one:
return .one(string: string)
case .two:
return .two(string: string)
}
}
}
let x = Example.one(string: "Hello")
print(x) // Prints one("Hello")
let y = x.cloneWith(string: "World")
print(y) // Prints one("World")

A single expression for guard case let with type casting of the associated type?

I have the following enum:
enum JSONData {
case dict([String:Any])
case array([Any])
}
I would like to do a pattern matching assignment with type casting using guard case let. I not only want to make sure that someData is .array, but that its associated type is [Int] and not just [Any]. Is this possible with a single expression? Something like the following:
let someData: JSONData = someJSONData()
guard case let .array(anArray as [Int]) = someData
else { return }
But the above does not compile; the error is downcast pattern value of type '[Int]' cannot be used. I know this is possible with the following but I'd rather do it in a single expression if possible.
guard case let .array(_anArray) = someData, let anArray = _anArray as? [Int]
else { return }
Edit on April 27, 2018: An update to this situation: you may now use the following syntax as long as the cast type is not a Collection:
enum JSONData {
case dict([String:Any])
case array(Any) // Any, not [Any]
}
func f() {
let intArray: JSONData = .array(1)
guard case .array(let a as Int) = intArray else {
return
}
print("a is \(a)")
}
f() // "a is 1"
If you attempt to use a collection type such as [Int], you receive the error error: collection downcast in cast pattern is not implemented; use an explicit downcast to '[Int]' instead.
What you're trying to do is not possible due to a limitation in Swift's pattern matching implementation. It's actually called out here:
// FIXME: We don't currently allow subpatterns for "isa" patterns that
// require interesting conditional downcasts.
This answer attempts to explain why, though falls short. However, they do point to an interesting piece of information that makes the things work if you don't use a generic as the associated value.
The following code achieves the one-liner, but loses some other functionality:
enum JSONData {
case dict(Any)
case array(Any)
}
let someData: JSONData = JSONData.array([1])
func test() {
guard case .array(let anArray as [Int]) = someData else {
return
}
print(anArray)
}
Alternatively, the same one liner can be achieved through a utility function in the enum definition that casts the underlying value to Any. This route preserves the nice relationship between the cases and the types of their associated values.
enum JSONData {
case dict([String : Any])
case array(Array<Any>)
func value() -> Any {
switch self {
case .dict(let x):
return x
case .array(let x):
return x
}
}
}
// This coercion only works if the case is .array with a type of Int
guard let array = someData.value() as? [Int] else {
return false
}

Can I change the Associated values of a enum?

I am reading the Swift tour document, and facing a problem.
Here is the code:
enum SimpleEnum {
case big(String)
case small(String)
case same(String)
func adjust() {
switch self {
case let .big(name):
name += "not"
case let .small(name):
name += "not"
case let .same(name):
name += "not"
}
}
}
The function adjust() won't work, I wonder if there is a way to change the Associated value of an enum, and how?
Your most immediate problem is that you're attempting to change the value of an immutable variable (declared with let) when you should be declaring it with var. This won't solve this particular problem though since your name variable contains a copy of the associated value, but in general this is something that you need to be aware of.
If you want to solve this, you need to declare the adjust() function as a mutating function and reassign self on a case by case basis to be a new enum value with an associated value composed from the old one and the new one. For example:
enum SimpleEnum{
case big(String)
case small(String)
case same(String)
mutating func adjust() {
switch self{
case let .big(name):
self = .big(name + "not")
case let .small(name):
self = .small(name + "not")
case let .same(name):
self = .same(name + "not")
}
}
}
var test = SimpleEnum.big("initial")
test.adjust()
switch test {
case let .big(name):
print(name) // prints "initialnot"
case let .small(name):
print(name)
case let .same(name):
print(name)
}