Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Created August 24, 2015 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriseidhof/625eeba5f568eb222149 to your computer and use it in GitHub Desktop.
Save chriseidhof/625eeba5f568eb222149 to your computer and use it in GitHub Desktop.
enum Request {
case ProductsList // "/products"
case Product(productID: Int) // "/products/:pid"
case CustomersList // "/customers"
case Customer(customerID: Int) // "/customers/:cid"
case OrdersList(customerID: Int) // "/customers/:cid/orders"
case Order(customerID: Int, orderID: Int) // "/customers/:cid/orders/:oid"
}
extension Array {
var decompose: (Element, [Element])? {
return isEmpty ? nil : (self[startIndex], Array(self.dropFirst()))
}
}
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C {
return { x in { y in f(x, y) } }
}
func curry<A, B, C, D>(f: (A, B, C) -> D)(_ x: A)(_ y: B)(_ z: C) -> D {
return f(x, y, z)
}
struct Match<A> {
let match: [String] -> (A, [String])?
init(raw x: [String] -> (A, [String])?) {
match = x
}
init(pure x: A) {
match = { (x, $0) }
}
}
extension Match {
func parse(components: [String]) -> A? {
guard case let (x, result)? = match(components) where result.isEmpty else { return nil }
return x
}
}
let int: Match<Int> = Match {
if let (x, rest) = $0.decompose,
let result = Int(x) {
return (result, rest)
}
return nil
}
/// Matches a constant string
func c(string: String) -> Match<String> {
return Match {
guard $0.first == string else { return nil }
return $0.decompose
}
}
/// Choice
infix operator <|> { associativity left precedence 130 }
func <|><A>(lhs: Match<A>, rhs: Match<A>) -> Match<A> {
return Match {
return lhs.match($0) ?? rhs.match($0)
}
}
infix operator <*> { associativity left precedence 150 }
func <*><A, B>(lhs: Match<A -> B>, rhs: Match<A>) -> Match<B> {
return Match { elements in
guard let (x, elements1) = lhs.match(elements) else { return nil }
guard let (y, elements2) = rhs.match(elements1) else { return nil }
return (x(y), elements2)
}
}
infix operator <* { associativity left precedence 150 }
func <*<A, B>(lhs: Match<A>, rhs: Match<B>) -> Match<A> {
return Match { elements in
guard let (x, newElements) = lhs.match(elements) else { return nil }
guard let (_, newerElements) = rhs.match(newElements) else { return nil }
return (x, newerElements)
}
}
func pure<A>(x: A) -> Match<A> { return Match { (x, $0) } }
let route =
Match(pure: Request.Product) <* c("products") <*> int <|>
Match(pure: Request.ProductsList) <* c("products") <|>
Match(pure: curry(Request.Order)) <* c("customers") <*> int <* c("orders") <*> int <|>
Match(pure: Request.Customer) <* c("customers") <*> int <|>
Match(pure: Request.CustomersList) <* c("customers")
let result = route.parse(["customers"])
print(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment