Last active
September 6, 2016 02:12
-
-
Save zhigang1992/2cf44bd1d50bd6d4924f956f5e27b9bc to your computer and use it in GitHub Desktop.
Argo Talk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// if you coming from the Argo Talk from WTB Swift | |
// here is the actual playground code: https://github.com/zhigang1992/ArgoTalk | |
import Foundation | |
struct Parser<T> { | |
let parse: (AnyObject) -> T? | |
} | |
extension Parser { | |
static func failed() -> Parser<T> { | |
return Parser { _ in nil } | |
} | |
static func unit(value:T) -> Parser<T> { | |
return Parser { _ in value } | |
} | |
func flatMap<U>(function: T-> Parser<U>) -> Parser<U> { | |
return Parser<U> { input in | |
self.parse(input).flatMap({function($0).parse(input)}) | |
} | |
} | |
func map<U>(function: T->U) -> Parser<U> { | |
return self.flatMap({.unit(function($0))}) | |
} | |
func apply<U>(applicative: Parser<T->U>) -> Parser<U> { | |
return applicative.flatMap({self.map($0)}) | |
} | |
} | |
let userData = [ | |
"id": 123, | |
"name": "Kyle Fang", | |
"gender": "male", | |
] | |
struct User { | |
enum Gender { | |
case Male | |
case Female | |
} | |
let id: Int | |
let name: String | |
let gender: Gender | |
} | |
let stringParser = Parser<String> { input in | |
input as? String | |
} | |
let numberPaser = Parser<Double> { input in | |
input as? Double | |
} | |
let intParser = numberPaser.map(Int.init) | |
let _newGenderParser: Parser<User.Gender> = stringParser.flatMap({ string in | |
if string == "male" { return .unit(.Male) } | |
if string == "female" { return .unit(.Female) } | |
return .failed() | |
}) | |
func or<T>(first:Parser<T>, _ second:Parser<T>) -> Parser<T> { | |
return Parser<T> { input in | |
if let firstValue = first.parse(input) { | |
return firstValue | |
} | |
return second.parse(input) | |
} | |
} | |
func parse<T>(string: String, `as` value: T) -> Parser<T> { | |
return stringParser.flatMap({ $0 == string ? .unit(value) : .failed() }) | |
} | |
infix operator <|> { associativity left } | |
func <|><T>(left:Parser<T>, right:Parser<T>) -> Parser<T> { | |
return or(left ,right) | |
} | |
let newGenderParser: Parser<User.Gender> = parse("male", as: .Male) | |
<|> parse("female", as: .Female) | |
<|> parse("guy", as: .Male) | |
<|> parse("dude", as: .Male) | |
func dictionaryParser<T>(key: String, parser: Parser<T>) -> Parser<T> { | |
return Parser<T> { input in | |
guard let input = input as? NSDictionary else { return nil } | |
return input[key].flatMap(parser.parse) | |
} | |
} | |
let assembleUser: Int -> String -> User.Gender -> User = { id in | |
{ name in | |
{ gender in | |
User(id: id, name: name, gender: gender) | |
} | |
} | |
} | |
let id = dictionaryParser("id", parser: intParser) | |
let name = dictionaryParser("name", parser: stringParser) | |
let gender = dictionaryParser("gender", parser: newGenderParser) | |
print(gender.apply(name.apply(id.map(assembleUser))).parse(userData)) | |
infix operator <^> { associativity left } | |
infix operator <*> { associativity left } | |
func <^><T, U>(left: T->U, right: Parser<T>) -> Parser<U> { | |
return right.map(left) | |
} | |
func <*><T, U>(left: Parser<T->U>, right: Parser<T>) -> Parser<U> { | |
return right.apply(left) | |
} | |
print((assembleUser <^> id <*> name <*> gender).parse(userData)) | |
func curry<A,B,C,D>(function: (A,B,C)->D) -> A -> B -> C -> D { | |
return { a in | |
{ b in | |
{ c in | |
function(a,b,c) | |
} | |
} | |
} | |
} | |
print((curry(User.init) <^> id <*> name <*> gender).parse(userData)) | |
curry(User.init) | |
<^> dictionaryParser("id", parser: intParser) | |
<*> dictionaryParser("name", parser: stringParser) | |
<*> dictionaryParser("gender", parser: newGenderParser) | |
//just going crazy and have fun here | |
infix operator <~~ { associativity left precedence 110 } | |
func <~~<T>(left: String, right: Parser<T>) -> Parser<T> { | |
return dictionaryParser(left, parser: right) | |
} | |
curry(User.init) | |
<^> "id" <~~ intParser | |
<*> "name" <~~ stringParser | |
<*> "gender" <~~ newGenderParser | |
protocol Parsable { | |
static var parser: Parser<Self> { get } | |
} | |
extension Int: Parsable { | |
static var parser: Parser<Int> { | |
return intParser | |
} | |
} | |
extension String: Parsable { | |
static var parser: Parser<String> { | |
return stringParser | |
} | |
} | |
extension User.Gender: Parsable { | |
static var parser: Parser<User.Gender> { | |
return newGenderParser | |
} | |
} | |
func <^><T:Parsable, U>(left: T->U, key: String ) -> Parser<U> { | |
return left <^> dictionaryParser(key, parser: T.parser) | |
} | |
func <*><T:Parsable, U>(left: Parser<T->U>, key: String) -> Parser<U> { | |
return left <*> dictionaryParser(key, parser: T.parser) | |
} | |
curry(User.init) <^> "id" <*> "name" <*> "gender" | |
extension User: Parsable { | |
static var parser: Parser<User> { | |
return curry(User.init) <^> "id" <*> "name" <*> "gender" | |
} | |
} | |
extension Parsable { | |
static func from(data:AnyObject) -> Self? { | |
return self.parser.parse(data) | |
} | |
} | |
User.from(userData) | |
let me:NSDictionary = [ | |
"id": 123, | |
"name": "Awesome Name", | |
"avatar": "http://www.google.com", | |
"followers": [ | |
[ | |
"id": 123, | |
"name": "Follower 1", | |
"gender": "male", | |
], | |
[ | |
"name": "Follower 2", | |
"id": 12345, | |
"gender": "Male", | |
] | |
] | |
] | |
struct Me { | |
let id: Int | |
let name: String | |
let avatar: String? | |
let followers: [User] | |
} | |
public func curry<A, B, C, D, E>(function: (A, B, C, D) -> E) -> A -> B -> C -> D -> E { | |
return { (`a`: A) -> B -> C -> D -> E in { (`b`: B) -> C -> D -> E in { (`c`: C) -> D -> E in { (`d`: D) -> E in function(`a`, `b`, `c`, `d`) } } } } | |
} | |
//curry(Me.init) | |
// <^> "id" | |
// <*> "name" | |
// <*> "avatar" | |
// <*> "followers" | |
func arrayParser<T>(parser:Parser<T>) -> Parser<[T]> { | |
return Parser<[T]> { input in | |
guard let input = input as? NSArray else { return nil } | |
return input.map(parser.parse).flatMap({$0}) | |
} | |
} | |
//let meParser = curry(Me.init) | |
// <^> "id" | |
// <*> "name" | |
// <*> "avatar" | |
// <*> "followers" <~~ arrayParser(User.parser) | |
// | |
//print(meParser.parse(me)) | |
func <^><T:Parsable, U>(left: [T]->U, key: String ) -> Parser<U> { | |
return left <^> dictionaryParser( | |
key, | |
parser: arrayParser(T.parser) | |
) | |
} | |
func <*><T:Parsable, U>(left: Parser<[T]->U>, key: String) -> Parser<U> { | |
return left <*> dictionaryParser( | |
key, | |
parser: arrayParser(T.parser) | |
) | |
} | |
func optionalParser<T>(parser:Parser<T>) -> Parser<Optional<T>> { | |
return Parser { input in | |
if let value = parser.parse(input) { | |
return Optional.Some(Optional.Some(value)) | |
} | |
return Optional.Some(Optional.None) | |
} | |
} | |
let newMeParser = curry(Me.init) | |
<^> "id" | |
<*> "name" | |
<*> optionalParser("avatar" <~~ stringParser) | |
<*> "followers" | |
func <^><T:Parsable, U>(left: Optional<T>->U, key: String ) -> Parser<U> { | |
return left <^> optionalParser(dictionaryParser( key,parser: T.parser)) | |
} | |
func <*><T:Parsable, U>(left: Parser<Optional<T>->U>, key: String) -> Parser<U> { | |
return left <*> optionalParser(dictionaryParser(key,parser: T.parser)) | |
} | |
let newNewMeParser = curry(Me.init) | |
<^> "id" | |
<*> "name" | |
<*> "avatar" | |
<*> "followers" | |
print(newNewMeParser.parse(me)) | |
// For nested dictionaries | |
["a", "b", "c"].reduce(stringParser, combine: {dictionaryParser($1, parser: $0)}) | |
// For Error Handling | |
enum ParseResult<T> { | |
case Success(T) | |
case Failed(String) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment