3

I'm hoping to achieve a certain style of enum syntax/functionality, however I'm not sure how to achieve it. Currently I have the following:

internal enum Timeframe: Equatable {

    // MARK: - Cases

    case hour(count: Int)
    case day(count: Int)
    case week(count: Int)
    case month(count: Int)
    case year(count: Int)
    case all
    case exact(date: Date)
    case unspecified
}

I would like to eliminate the count associated value, unless it's required. For example:

let oneDayTimeframe: Timeframe = .day
let twoDayTimeframe: Timeframe = .day.multiplied(by: 2)

Is this possible? Even if there isn't a way to achieve exactly what I'm looking for, I'd appreciate suggestions for potential improvements. In most cases, I end up using (count: 1), which seems a bit verbose. If default values were available with associated values, I would have used

case hour(count: Int = 1)

Any suggestions?

user3746428
  • 11,047
  • 20
  • 81
  • 137

2 Answers2

3

Iterating a bit on the answer you provided:

enum TimeFrame: Equatable {

    case all
    case countable(timeFrame: CountableTimeFrame)
    case exact(date: Date)
}

enum CountableTimeFrame: Equatable {

    case hour
    case day
    case week
    case month
    case year

    indirect case multiple(CountableTimeFrame, Int)

    var timeFrame: TimeFrame {
        return .countable(timeFrame: self)
    }

    static func * (left: CountableTimeFrame, right: Int) -> CountableTimeFrame {
        switch left {
        case .multiple(let timeFrame, let count):
            return .multiple(timeFrame, count * right)
        default:
            return .multiple(left, right)
        }
    }

    static func * (left: Int, right: CountableTimeFrame) -> CountableTimeFrame {
        return right * left
    }
}

Would disallow misuse e.g. disallow:

let timeFrame: TimeFrame = .multiple(.exact(date: someDate), 666)
let timeFrame: TimeFrame = .multiple(unspecified, 40)
let timeFrame: TimeFrame = .multiple(all, -1) 

And allow multiplication using the * operator, e.g.

let timeFrame: CountableTimeFrame = 4 * .hour
print(timeFrame) // multiple(__lldb_expr_5.CountableTimeFrame.hour, 4)
print(timeFrame * 2) // multiple(__lldb_expr_5.CountableTimeFrame.hour, 8)

And .unspecified:

let optionalTimeFrame: TimeFrame? = nil
l_priebe
  • 774
  • 4
  • 17
  • Thanks! I really like this solution. It's unfortunate that you lose the ability to create singular timeframes with `TimeFrame.day`, but definitely a more robust implementation overall. – user3746428 Mar 05 '19 at 21:47
0

I discovered that this is possible using recursive enums. Although not exactly the same as I suggested, this achieves a similar result:

internal enum Timeframe: Equatable {

    // MARK: - Cases

    case hour
    case day
    case week
    case month
    case year
    case all
    case exact(date: Date)
    case unspecified

    // MARK: - Indirect Cases

    indirect case multiple(Timeframe, Int)
}
user3746428
  • 11,047
  • 20
  • 81
  • 137
  • 1
    A disadvantage to this and the way the enum is structured is that .multiple(.exact(date: someDate), 666) or .multiple(unspecified, 40) or .multiple(all, -1) would compile just fined but makes no sense at all. – l_priebe Mar 02 '19 at 12:52