Skip to content

Instantly share code, notes, and snippets.

@rnapier
Created September 7, 2014 18:20
Show Gist options
  • Save rnapier/067156ac404cc42f17b6 to your computer and use it in GitHub Desktop.
Save rnapier/067156ac404cc42f17b6 to your computer and use it in GitHub Desktop.
Version 2 of Flattenin' Your Mappenin'
//
// Version 2 of pagesFromData from Flattenin' Your Mappenin'
// http://robnapier.net/flatmap
//
import Foundation
infix operator >>== {}
func >>== <T,U>(x: T, f:T -> Result<U>) -> Result<U> {
return x.flatMap(f)
}
func pagesFromData(data: NSData) -> Result<[Page]> {
return asJSON(data) >>== asJSONArray
>>== secondElement >>== asStringList
>>== asPages
}
func asJSON(data: NSData) -> Result<JSON> {
var error: NSError?
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &error)
switch (json, error) {
case (_, .Some(let error)): return .Failure(error)
case (.Some(let json), _): return .Success(Box(json))
default:
fatalError("Received neither JSON nor an error")
return .Failure(NSError())
}
}
func asJSONArray(json: JSON) -> Result<JSONArray> {
if let array = json as? JSONArray {
return .Success(Box(array))
} else {
return .Failure(NSError(localizedDescription: "Expected array. Got: \(json)"))
}
}
func secondElement(array: JSONArray) -> Result<JSON> {
if array.count < 2 {
return .Failure(NSError(localizedDescription:"Could not get second element. Array too short: \(array.count)"))
}
return .Success(Box(array[1]))
}
func asStringList(array: JSON) -> Result<[String]> {
if let string = array as? [String] {
return .Success(Box(string))
} else {
return .Failure(NSError(localizedDescription: "Unexpected string list: \(array)"))
}
}
func asPages(titles: [String]) -> Result<[Page]> {
return .Success(Box(titles.map { Page(title: $0) }))
}
enum Result<A> {
case Success(Box<A>)
case Failure(NSError)
func flatMap<B>(f:A -> Result<B>) -> Result<B> {
switch self {
case Success(let value): return f(value.unbox)
case Failure(let error): return .Failure(error)
}
}
}
final class Box<T> {
let unbox: T
init(_ value: T) { self.unbox = value }
}
extension Result: Printable {
var description: String {
switch self {
case .Success(let box):
return "Success: \(box.unbox)"
case .Failure(let error):
return "Failure: \(error.localizedDescription)"
}
}
}
struct Page {
let title: String
}
extension Page: Printable {
var description: String {
return title
}
}
typealias JSON = AnyObject
typealias JSONArray = [JSON]
extension NSError {
convenience init(localizedDescription: String) {
self.init(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: localizedDescription])
}
}
func asJSONData(string: NSString) -> NSData {
return string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
}
let goodPagesJson = asJSONData("[\"a\",[\"Animal\",\"Association football\",\"Arthropod\",\"Australia\",\"AllMusic\",\"African American (U.S. Census)\",\"Album\",\"Angiosperms\",\"Actor\",\"American football\",\"Austria\",\"Argentina\",\"American Civil War\",\"Administrative divisions of Iran\",\"Alternative rock\"]]")
pagesFromData(goodPagesJson).description
let corruptJson = asJSONData("a\",[\"Animal\",\"Association football\",\"Arthropod\",\"Australia\",\"AllMusic\",\"African American (U.S. Census)\",\"Album\",\"Angiosperms\",\"Actor\",\"American football\",\"Austria\",\"Argentina\",\"American Civil War\",\"Administrative divisions of Iran\",\"Alternative rock\"]]")
pagesFromData(corruptJson).description
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment