Created
January 31, 2017 21:10
-
-
Save alonecuzzo/b554918f01ef5087612d843feb10d44c to your computer and use it in GitHub Desktop.
Notes from Errors chat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//let's talk about the different error approaches in swift | |
//1. optionals | |
//2. throw/catch | |
//3. Result | |
enum Result<A> { | |
case failure(Error) | |
case success(A) | |
} | |
//file reader | |
func contentsOrNil(ofFile filename: String) -> String? | |
enum FileError: Error { | |
case fileDoesNotExist | |
case noPermission | |
} | |
func contents(ofFile filename: String) -> Result<String> | |
let result = contents(ofFile: "lol.txt") | |
switch result { | |
case let .success(contents): | |
//succeed w/ file contents | |
case let .failure(error) | |
//fail with error | |
} | |
//throwing & catching | |
func contents(ofFile filename: String) throws -> String | |
//forces handling of error at code time | |
do { | |
let result = try contents(ofFile: "lol.txt") | |
//success | |
} catch FileError.fileDoesNotExist { | |
//handle that error | |
} catch { | |
//all other cases | |
} | |
enum ParseError: Error { | |
case wrongEncoding | |
case warning(line: Int, message: String) | |
} | |
do { | |
let result try parse(text: "") | |
//success | |
} catch ParseError.wrongEncoding { | |
// | |
} catch let ParseError.warning(line, message) { | |
//warning | |
} catch {} | |
//swift can't see the type of error that is being thrown across modules | |
//can't specify which errors that it'll show | |
//design decision - most times only want to know if an error has been thrown | |
//would complicate function signatures | |
//adding an error case would break all api clients | |
//because errors are untyped, important to document what error types a function can thrown | |
enum Result<A, ErrorType: Error> { | |
case failure(ErrorType) | |
case success(A) | |
} | |
//when your errors have significant semantic meaning, a typed Result is better than built in swift error handling | |
// a plus for using the try/catch is that it reminds you to handle an error case when there is no return | |
func setupServerConnection() throws | |
//defer | |
func contents(ofFile filename: String) throws -> String { | |
let file = open(filename, O_RDONLY) | |
defer { close(file) } | |
let contents = try process(file: file) | |
return contents | |
} | |
//can allow you to put initialization & cleanup parts of your code closer together - makes the code more readable | |
func next() -> Element? { | |
guard let b = _base.next() else { return nil } | |
defer { _count += 1 } | |
return (offset: _count, element: b) | |
} | |
//def runs in reverse order like a stack | |
//segfaults or fatalErrors skip the defers | |
guard let db = openDB() else { return } | |
defer { closeDatabase(db) } | |
guard let connection = openConnection(db) else { return } | |
defer { closeConnection(connection) } | |
guard let result = runQuery(connection...) else { return } | |
//Errors and optionals -- try? | |
if let result = try? parse(text: input) { | |
//do sumthing w/ result | |
} | |
//convert from optional to error | |
extension Optional { | |
func or(error: Error) throws -> Wrapped { | |
switch self { | |
case self: | |
case let x?: return x | |
case nil: throw error | |
} | |
} | |
} | |
//then can do | |
do{ | |
let int = try Int("42").or(error: ReadIntError.couldNotRead) | |
} catch {} | |
//try? might seem contradictory but it still forces you to acknowledge your actions | |
//chaining errors | |
//swift way is super easy/readable | |
func checkFilesAndProcessID(filenames: [String]) -> Int { | |
do { | |
try filenames.all(condition: checkFile) | |
let pidString = try contents(ofFile: "pidFile") | |
return try Int(pidString).or(error: ReadIntError.couldNotRead) | |
} catch { | |
return 42 | |
} | |
} | |
extension Result { | |
func flatMap<B>(transform: (A) -> Result<B>) -> Result<B> { | |
switch self { | |
case let .failure(m): return .failure(m) | |
case let .success(x): return transform(x) | |
} | |
} | |
} | |
func checkFilesAndProcessID(filenames: [String]) -> Result<Int> { | |
return filenames.all(condition: checkFile) | |
.flatMap { _ in contents(ofFile: "pidfile") } | |
.flatMap { contents in | |
Int(contents).map(Result.success) ?? .failure(ReadIntError.couldNotRead) | |
} | |
} | |
//higher order functions - where swift's system doesn't work so well | |
func compute(callback: (Int) -> ()) | |
func compute(callback: (Int?) -> ()) //might fail | |
func computeThrows(callback: Int -> throws ()) | |
//distinction here is that instead of saying that the computation might fail | |
//it says the callback could throw an error | |
//Result & Optional work on types, throws works on function types - means that the FUNCTION might fail | |
//with result | |
func computeResult(callback: (Result<Int>) -> ()) | |
//with throws | |
func compute(callback: (() throws -> Int) -> ()) | |
compute { (resultFunc: () throws -> Int) in | |
do { | |
let result = try resultFunc() | |
} catch {} | |
} | |
//result type was proposed | |
//https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001433.html |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment