blob: 320e1eea30df496183cf560b363012c63efb6e92 [file] [log] [blame]
//===--- Result.swift -----------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// RUN: %target-run-stdlib-swift
// REQUIRES: executable_test
public enum Result<Value> {
case Success(Value)
case Error(Error)
init(success x: Value) {
self = .Success(x)
}
init(error: Error) {
self = .Error(error)
}
func map<U>(_ transform: (Value) -> U) -> Result<U> {
switch self {
case .Success(let x): return .Success(transform(x))
case .Error(let e): return .Error(e)
}
}
func flatMap<U>(_ transform: (Value) -> Result<U>) -> Result<U> {
switch self {
case .Success(let x): return transform(x)
case .Error(let e): return .Error(e)
}
}
func get() throws -> Value {
switch self {
case .Success(let x): return x
case .Error(let e): throw e
}
}
var success: Value? {
switch self {
case .Success(let x): return x
case .Error: return nil
}
}
var error: Error? {
switch self {
case .Success: return nil
case .Error(let x): return x
}
}
}
public func ?? <T> (
result: Result<T>, defaultValue: @autoclosure () -> T
) -> T {
switch result {
case .Success(let x): return x
case .Error: return defaultValue()
}
}
// We aren't actually proposing this overload; we think there should
// be a compiler warning that catches the promotion that you probably
// don't want.
public func ?? <T> (
result: Result<T>?, defaultValue: @autoclosure () -> T
) -> T {
fatalError("We should warn about Result<T> being promoted to Result<T>?")
}
/// Translate the execution of a throwing closure into a Result
func catchResult<Success>(
invoking body: () throws -> Success
) -> Result<Success> {
do {
return try .Success(body())
}
catch {
return .Error(error)
}
}
// A couple of error types
enum Nasty : Error {
case Bad, Awful, Terrible
}
enum Icky : Error {
case Sad, Bad, Poor
}
// Some Results to work with
let three = Result(success: 3)
let four = Result(success: 4)
let nasty = Result<Int>(error: Nasty.Bad)
let icky = Result<String>(error: Icky.Sad)
print(three)
print(nasty)
print(icky)
print(three ?? 4)
print(nasty ?? 4)
print(three.map { String($0) })
print(nasty.map { String($0) })
print(three.flatMap { .Success(String($0)) })
print(nasty.flatMap { .Success(String($0)) })
print(three.flatMap { _ in icky })
print(nasty.flatMap { _ in icky })
try print(three.get())
do {
try print(nasty.get())
}
catch {
print(error)
}
func mayFail(_ fail: Bool) throws -> Int {
if fail { throw Icky.Poor }
return 0
}
print(catchResult { try mayFail(true) })
print(catchResult { try mayFail(false) })
print(catchResult { 1 }.flatMap { _ in Result(success: 4) }.flatMap { _ in Result<String>(error: Icky.Poor) })
print(catchResult { 1 }.map { _ in three }.flatMap {$0} )
let results = [three, nasty, four]
print(results.flatMap { $0.success })
print(results.flatMap { $0.error })
print(results.contains { $0.success != nil })
// Mistaken usage; causes Result<T> to be promoted to Result<T>?
// print(three ?? nasty)