Suppose that Either is declared in the following way,
enum Either<T, U> {
case Left(T)
case Right(U)
}I want language-level support for Either in a consistent way with for Optional.
Int? is a syntactic sugar of Optional<Int>.
// Optional
let o1: Optional<Int> = .Some(42)
let o2: Int? = .Some(42)It seems good that String|Int is a syntactic sugar of Either<String, Int>.
// Either
let e1: Either<String, Int> = .Right(42)
let e2: String|Int = .Right(42)
Foo|Bar is a familiar notation because it is used for Union Types in some languages. (e.g. TypeScript, Ceylon and Flow)
// Optional
let o1: Int? = .Some(42)
let o2: Int? = 42// Either
let e1: String|Int = .Right(42)
let e2: String|Int = 42Int is a subtype of Optional<Int> in Swift.
// Optional
class A {
func foo() -> Int? {
return nil
}
}
class B: A {
// changes the return type to Int
override func foo() -> Int {
return 42
}
}Int may be a subtype of Either<String, Int>.
// Either
class A {
func foo() -> String|Int {
return "error
}
}
class B: A {
// changes the return type to Int
override func foo() -> Int {
return 42
}
}
Then Either works like Union Types even though it is an Enumeration. It is consistent with Optional.
// Optional
let foo: Int? = 42
let bar: Double? = foo?.bar// Either
let foo: String|Int = 42
let bar: String|Double = foo?.bar// Optional
let foo: Int? = 42
let bar: Double = foo!.bar// Either
let foo: String|Int = 42
let bar: Double = foo!.bar// Optional
let fooOrNil: Int? = 42
if let foo: Int = fooOrNil {
...
}// Either
let fooOrError: String|Int = 42
if let foo: Int = fooOrError {
...
}// Optional
let foo: Int? = try? foo("42")// Either
let foo: Error|Int = try| foo("42")// Optional
class Foo {
let x: Int
init?(x: String) {
do {
x = try foo(x)
} catch {
return nil
}
}
}
let foo: Foo? = Foo(x: 42)// Either
class Foo {
init|(x: String) {
do {
x = try foo(x)
} catch let error as Error {
return .Left(error)
}
}
}
let foo: Error|Foo = Foo(x: 42)Although it does not need language-level support for ?? because it is just a function.
// Optional
let foo: Int? = nil
let foo2: Int = foo ?? 42// Either
let foo: String|Int = "error"
let bar: Int = foo ?? 42Also it does not need language-level support for map and flatMap. But I must refer to them because they are necessary.
// Optional
let o1: Int? = 42
let o2: Int? = nil
let square: Int? = o1.map { $0 * $0 }
let mean: Int? = o1.flatMap { x in o2.flatMap { y in (x + y) / 2 } }// Either
let e1: String|Int = 42
let e2: String|Int = "error"
let square: String|Int = e1.map { $0 * $0 }
let mean: String|Int = e1.flatMap { x in e2.flatMap { y in (x + y) / 2 } }Foo|Bar|Baz can be interpreted as both Either<Either<Foo, Bar>, Baz> and Either<Foo, Either<Bar, Baz>>. I think the former one is better if we mainly use Either for error handlings. Because I want Int as a result of foo! in the following code.
let fooOrError: ErrorA|ErrorB|Int = ...
let foo: Int = fooOrError!We also need some notations like Foo|(Bar|Baz) in order to specify Either<Foo, Either<Bar, Baz>>.