Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with recursive enums in Swift 5.1

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

Screen

I think something is going wrong in the last line of code.

What does that mean?

like image 229
Sam Avatar asked Oct 15 '25 07:10

Sam


1 Answers

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:

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

like image 137
Alexander Avatar answered Oct 18 '25 05:10

Alexander



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!