Skip to content

Instantly share code, notes, and snippets.

@drodriguez
Created June 14, 2014 19:48
Show Gist options
  • Save drodriguez/649bd0c45a2d2fa8b37f to your computer and use it in GitHub Desktop.
Save drodriguez/649bd0c45a2d2fa8b37f to your computer and use it in GitHub Desktop.
import Foundation
enum Either<A, B> {
case Left(@auto_closure () -> A)
case Right(@auto_closure () -> B)
func fold<X>(fa: A -> X, fb: B -> X) -> X {
switch self {
case let .Left(a): return fa(a())
case let .Right(b): return fb(b())
}
}
var left: LeftProjection<A, B> { return LeftProjection<A, B>(e: self) }
var right: RightProjection<A, B> { return RightProjection<A, B>(e: self) }
func leftOrElse(b: @auto_closure () -> A) -> A {
return fold({ $0 }, { _ in b() })
}
func rightOrElse(a: @auto_closure () -> B) -> B {
return fold({ _ in a() }, { $0 })
}
}
struct LeftProjection<A, B> {
let e: Either<A, B>
func map<X>(f: A -> X) -> Either<X, B> {
return e.fold({ a in Either<X, B>.Left(f(a)) }, { Either<X, B>.Right($0) })
}
}
struct RightProjection<A, B> {
let e: Either<A, B>
func map<X>(f: B -> X) -> Either<A, X> {
return e.fold({ Either<A, X>.Left($0) }, { b in Either<A, X>.Right(f(b)) })
}
}
func divOrError(a: Int, b: Int) -> Either<NSError, Int> {
if b == 0 {
let e = NSError(domain: "MyDomain", code: -1, userInfo: NSDictionary())
return Either.Left(e)
} else {
return Either.Right(a / b)
}
}
// You can extract the left or right value using fold:
divOrError(4, 2).fold(
{ println("There was an error: \($0)") },
{ println("The result is \($0)") }
)
// => "The result is 2"
divOrError(7, 0).fold(
{ println("There was an error: \($0)") },
{ println("The result is \($0)") }
)
// => "There was an error: ..."
// You can use left or right to only act over a Left or a Right, without
// modifying the other, but still maintaining everything in an Either
let l = Either<String, Int>.Left("hello world!")
let r = Either<String, Int>.Right(1234)
let x = l.left.map { countElements($0) } // => Either<Int, Int> = Left(12)
let y = r.left.map { countElements($0) } // => Either<Int, Int> = Right(1234)
let z = l.right.map { Double($0) } // => Either<String, Double> = Left("hello world!")
let w = r.right.map { Double($0) } // => Either<String, Double> = Right(1234.0)
// Useful if you just want to do something in case there is no error, or return
// the underlaying error otherwise
func divAddThreeOrError(a: Int, b: Int) -> Either<NSError, Int> {
return divOrError(7, 0).right.map { $0 + 3 }
}
divAddThreeOrError(7, 0).fold(
{ println("There was an error: \($0)") },
{ println("The result is \($0)") }
)
// => "There was an error: ..."
// In case of error use another value
let v = divOrError(7, 0).rightOrElse(42)
println("The result is \(v)") // => "The result is 42"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment