Skip to content

Instantly share code, notes, and snippets.

@alonecuzzo
Created January 31, 2017 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alonecuzzo/b554918f01ef5087612d843feb10d44c to your computer and use it in GitHub Desktop.
Save alonecuzzo/b554918f01ef5087612d843feb10d44c to your computer and use it in GitHub Desktop.
Notes from Errors chat
//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