because when we make // a URL route chaining, previous route doesn't // affect the next route (context-free grammar). // // In other word, there is no need to create // a new `Route` at runtime, // and only applying existing routes // one by one sequentially is sufficient.
enum Route<Value> { case match(String, Route<Value>) case capture((String) -> Any?, (Any) -> Route<Value>) case choice([Route<Value>]) case term(Value) case zero } • .match contains next Route • .capture contains "parsing" and "converting" functions
{ switch self { case let .match(string, route): ... case let .capture(parse, convert): ... case let .choice(routes): ... case let .term(value): return value case .zero: return nil } } } // Implement `run`, Functor, Applicative, Alternative...
{ case let .match(string, route): return .match(string, optimize(route)) case let .capture(parse, convert): return .capture(parse, { optimize(convert($0)) }) case let .choice(routes): return _optimizeChoice(routes) case .term, .zero: return route } }
if routes.isEmpty { return .zero } // Sort, group by the same route-head, and flatten. let sortedRoutes = routes.sorted(by: isAscending) let arrangedRoutes = groupBy(isSameRouteHead)(ArraySlice(sortedRoutes)) .map(flatten) return choice(arrangedRoutes) }
into Monadic Applicative parser • Optimization is possible with interpretable enum pattern • Caveat • Too easy to get error: "Expression was too complex to be solved in reasonable time." • Swift 4 will surely solve this problem!