Skip to content

Instantly share code, notes, and snippets.

@zhigang1992
Last active September 6, 2016 02:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhigang1992/2cf44bd1d50bd6d4924f956f5e27b9bc to your computer and use it in GitHub Desktop.
Save zhigang1992/2cf44bd1d50bd6d4924f956f5e27b9bc to your computer and use it in GitHub Desktop.
Argo Talk
// 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