Home > Software design >  Is there a Swift-er way to refer to an enum case as a 'type' that can by type-checked?
Is there a Swift-er way to refer to an enum case as a 'type' that can by type-checked?

Time:08-09

I'm trying to develop a parser that works off of Swift enums that represent tokens. I would like to be able to validate that a token is one of a certain type (e.g. in this case a certain case of an enum). Looking at the Swift docs, a way to do this without an additional enum isn't apparent to me, and I'm not optimistic. Is the following as close I can get to a more succinct pure Swift language-level solution?

enum SymbolTokenType {
    case river, lake, mountain, meadow, valley, openParen, closeParen, logicalAnd, logicalOr, logicalNot
}

enum SymbolToken {
    case river              (key: RiverKey?)
    case lake               (key: LakeKey?)
    case mountain           (key: MountainKey?)
    case meadow             (key: MeadowKey?)
    case valley             (key: ValleyKey?)
    case openParen
    case closeParen
    case logicalAnd
    case logicalOr
    case logicalNot
    
    func validateTokenType(validTypes: [SymbolTokenType] ) -> Bool {
        switch(self) {
        case .river:
            return validTypes.contains(.river)
        case .lake:
            return validTypes.contains(.lake)
        case .mountain:
            return validTypes.contains(.mountain)
        case .meadow:
            return validTypes.contains(.meadow)
        case .valley:
            return validTypes.contains(.valley)
        case .openParen:
            return validTypes.contains(.openParen)
        case .closeParen:
            return validTypes.contains(.closeParen)
        case .logicalAnd:
            return validTypes.contains(.logicalAnd)
        case .logicalOr:
            return validTypes.contains(.logicalOr)
        case .logicalNot:
            return validTypes.contains(.logicalNot)
        }
    }
}

CodePudding user response:

I would do like this:

enum Token {
    case token1
    case token2
    case token3
    
    func isValid(from container: [Token]) -> Bool {
        container.contains(self)
    }
}

let validTokens: [Token] = [.token1, .token2]

let testedToken2 = Token.token2
let testedToken3 = Token.token3

testedToken2.isValid(from: validTokens) // True
testedToken3.isValid(from: validTokens) // False

CodePudding user response:

You definitely don't need two enums. You just have to deal with cases with associated values a bit differently.

protocol SimilarComparable: Equatable {
    func isSimilar(to other: Self) -> Bool
    func isSimilar(toAny others: [Self]) -> Bool
}

extension SimilarComparable {
    func isSimilar(toAny others: [Self]) -> Bool {
        let result = others.first(where: { $0.isSimilar(to: self) }) != nil
        print("Finding similarity to \(self) in \(others) == \(result)")
        return result
    }
}

enum SymbolToken: SimilarComparable {

    case river              (key: String?)
    case lake               (key: String?)
    case mountain           (key: String?)
    case meadow             (key: String?)
    case valley             (key: String?)
    case openParen
    case closeParen
    case logicalAnd
    case logicalOr
    case logicalNot

    func isSimilar(to other: SymbolToken) -> Bool {

        let result: Bool
        print("Finding similarit to \(self) to \(other) == ", separator: " ", terminator: "")
        switch (self, other) {
        case (.river, .river):
            result = true
        case (.lake, .lake):
            result = true
        case (.mountain, .mountain):
            result = true
        case (.meadow, .meadow):
            result = true
        case (.valley, .valley):
            result = true
        default:
            result = self == other
        }
        print("\(result)")
        return result
    }
}

Usage would be like this:

_ = SymbolToken.river(key: "Hudson").isSimilar(to: .river(key: "Nile")) // true
_ = SymbolToken.lake(key: "Michigan").isSimilar(to: .lake(key: "Tahoe")) // true
_ = SymbolToken.closeParen.isSimilar(to: .closeParen) // true
_ = SymbolToken.logicalOr.isSimilar(to: .logicalOr) // true
_ = SymbolToken.logicalOr.isSimilar(to: .logicalAnd) // false

let tokens: [SymbolToken] = [
    .river(key: "Hudson"),
    .lake(key: "Tahoe"),
    .closeParen,
    .logicalOr,
]
_ = SymbolToken.logicalOr.isSimilar(toAny: tokens) // true
_ = SymbolToken.lake(key: "Michigan").isSimilar(toAny: tokens) // true
  • Related