Skip to content

Instantly share code, notes, and snippets.

@p69
Last active June 12, 2019 06:11
Show Gist options
  • Save p69/0bc4c3a7e1cd64b77139057a42e48973 to your computer and use it in GitHub Desktop.
Save p69/0bc4c3a7e1cd64b77139057a42e48973 to your computer and use it in GitHub Desktop.
import Cocoa
precedencegroup PipeOperatorPrecedence {
associativity: left
higherThan: LogicalConjunctionPrecedence
}
infix operator |>: PipeOperatorPrecedence
public func |> <A, B>(arg: A,
f: @escaping (A) -> B) -> B {
return f(arg)
}
precedencegroup KleisliOperatorPrecedence {
associativity: left
higherThan: PipeOperatorPrecedence
}
infix operator >=>: KleisliOperatorPrecedence
public enum Result<T, E> {
case ok(T)
case error(E)
}
extension Result {
public func bind<B>(_ f: @escaping (T) -> Result<B, E>) -> Result<B, E> {
switch self {
case .ok(let x):
return f(x)
case .error(let e):
return Result<B, E>.error(e)
}
}
}
public func >=> <A, B, C, E>(f: @escaping (A) -> Result<B, E>,
g: @escaping (B) -> Result<C, E>) -> (A)->Result<C, E> {
return { a in
f(a).bind(g)
}
}
struct UserInput {
let email: String
let password: String
}
struct User {
let id = UUID.init()
let email: String
}
extension User: CustomDebugStringConvertible {
var debugDescription: String {
return "{ id: \(id), email: \(email) }"
}
}
enum UserError: Error {
case invalidEmail, invalidPassword, alreadyExist
case unknown(cause: Error)
}
//key-value as DB store
var usersDb = [String: User]()
//simulate DB runtime error
enum DbError: Error {
case duplicateKeyError
}
func saveToDb(_ input: UserInput) throws -> User {
if usersDb[input.email] != nil {
print("user \(input.email) already exists")
throw DbError.duplicateKeyError
}
let newUser = User(email: input.email)
usersDb[input.email] = newUser
print("user saved: \(newUser)")
return newUser
}
func validateEmail(input: UserInput) -> Result<UserInput, UserError> {
//Simple regular expression for email validation
let emailPredicate = NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}")
//Validate email
return emailPredicate.evaluate(with: input.email) ? .ok(input) : .error(UserError.invalidEmail)
}
func validatePassword(input: UserInput) -> Result<UserInput, UserError> {
return input.password.count > 6 ? .ok(input) : .error(UserError.invalidPassword)
}
func saveToDbWithResult(input: UserInput) -> Result<User, UserError> {
do {
let user = try saveToDb(input)
return .ok(user)
} catch DbError.duplicateKeyError {
return .error(UserError.alreadyExist)
} catch {
return .error(UserError.unknown(cause: error))
}
}
func register(input: UserInput) -> Result<User, UserError> {
return validateEmail(input: input)
.bind(validatePassword)
.bind(saveToDbWithResult)
}
func registerOperators(input: UserInput) -> Result<User, UserError> {
return input
|> validateEmail
>=> validatePassword
>=> saveToDbWithResult
}
let test1 = UserInput(email: "123@mail.com", password: "password")
let registrationResult = register(input: test1)
switch registrationResult {
case .ok(let user):
print("user \(user) successfully registered")
case .error(let error):
print("failed to register new user. Error: \(error)")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment