Exceptions: try, catch, handle, finally, a dispatcher, try with resources and try guards
The complete source of exceptions_big_cats.ek9, compiled by the EK9 compiler. Shown on exceptions.html.
#!ek9 defines module site.exceptions.bigCats defines constant missingFile <- "MightNotExist.txt" defines type BigCat Lion Tiger WildCat Leopard Lynx defines class AnException extends Exception supportingInformation <- String() AnException() -> primaryReason as String supportingInformation as String exitCode as Integer super(primaryReason, exitCode) this.supportingInformation :=: supportingInformation supportingInformation() as pure <- rtn as String: supportingInformation override operator $ as pure <- rtn as String: `${reason()} ${supportingInformation()} exit code ${exitCode()}` default operator ? default operator $$ OtherException extends Exception retryAfter <- DateTime() OtherException() -> reason as String retryAfter as DateTime super(reason) this.retryAfter :=: retryAfter retryAfter() as pure <- rtn as DateTime: retryAfter override operator $ as pure <- rtn as String: `${reason()} retry after ${$retryAfter()}` default operator ? default operator $$ ExceptionExample clock as Clock: SystemClock() deferProcessingUntilAfter <- DateTime() default private ExceptionExample() ExceptionExample() -> clock as Clock this.clock: clock checkExceptionHandling() -> aCat as BigCat <- didProcess <- false stdout <- Stdout() try if deferProcessing() stdout.println(`Deferred until after ${deferProcessingUntilAfter} ${aCat} not processed`) else triggerResult <- triggerPossibleException(aCat) stdout.println(triggerResult) didProcess: true catch -> ex as Exception stdout.println(handleException(ex)) finally stdout.println(`Finished checking ${aCat}`) triggerPossibleException() -> aCat as BigCat <- rtn as String: String() switch aCat case BigCat.Lion throw Exception($aCat, 1) case BigCat.Tiger throw AnException("Too dangerous", $aCat, 2) case BigCat.WildCat, BigCat.Lynx rtn: `Success with ${aCat}` case BigCat.Leopard throw OtherException($aCat, clock.dateTime() + PT2H) default rtn: `Success with ${aCat}` deferProcessing() <- rtn as Boolean: deferProcessingUntilAfter? <- deferProcessingUntilAfter > clock.dateTime() else false private handleException() as dispatcher -> ex as Exception <- rtn as String: $ex private handleException() -> ex as AnException <- rtn as String: $ex if ex.exitCode()? tidyUpReadyForProgramExit() private handleException() -> ex as OtherException <- rtn as String: $ex this.deferProcessingUntilAfter: ex.retryAfter() private tidyUpReadyForProgramExit() Stdout().println("Would tidy up any state ready for program exit") default operator ? FileExceptionExample demonstrateFileNotFound() stdout <- Stdout() //A failure while ACQUIRING a resource is not handled by the try that declares it; //it goes to the enclosing try. Once acquired, a resource is always closed. try try -> input <- openInput(missingFile) cat input > stdout //rather than use catch 'handle' can be used handle -> ex as Exception stdout.println($ex) finally stdout.println("Automatically closed file if opened") demonstrateNamedFileNotFound() -> fileName as String mainResults <- try -> input1 <- openInput(fileName) <- rtn as List of String: cat input1 | collect as List of String //Let the exceptions fly back - don't handle in here. Stdout().println(`Main Results ${mainResults}`) demonstrateFilesNotFound() mainResults <- try -> input1 <- openInput(missingFile) input2 <- openInput("AlsoMightNotExist.txt") <- rtn as List of String: cat input1, input2 | collect as List of String //Let the exceptions fly back - don't handle in here. Stdout().println(`Main Results ${mainResults}`) defines function //A missing file is not an Exception in EK9: the TextFile is just unset. //Here that is turned into an Exception, so the caller decides what it means. openInput() -> fileName as String <- rtn as CloseableStringInput? file <- TextFile(FileSystemPath().inCurrentWorkingDirectory(fileName)) if not file? throw Exception(`File Not Found: ${fileName}`) rtn: file.input() defines program TryCatchExample() stdout <- Stdout() //Rather than use SystemClock - simulate one so that date time can be altered. //This is a way of doing a 'Fake'/'Stub'. simulatedClock <- () with trait of Clock as class currentDateTime as DateTime: 1971-02-01T12:00:00Z override dateTime() as pure <- rtn as DateTime: currentDateTime override millisecond() as pure <- rtn as Millisecond: Millisecond(0) setCurrentDateTime() -> newDateTime as DateTime this.currentDateTime = newDateTime override operator ? as pure <- rtn <- true //use the simulated clock example1 <- ExceptionExample(simulatedClock) for cat in BigCat if example1.checkExceptionHandling(cat) stdout.println(`Processing of ${cat} was completed`) else stdout.println(`Processing of ${cat} was NOT completed`) //just try Lynx again require not example1.checkExceptionHandling(BigCat.Lynx) //alter the time just passed the retry after time. simulatedClock.setCurrentDateTime(simulatedClock.dateTime() + PT2H1M) //Now it should be processed. require example1.checkExceptionHandling(BigCat.Lynx) example2 <- FileExceptionExample() example2.demonstrateFileNotFound() try example2.demonstrateFilesNotFound() catch -> ex as Exception stdout.println(`TryCatchExample: ${ex}`) defines function knownFileName() -> key as String <- rtn as String: String() if key == "report" rtn: missingFile defines program TryWithGuard() stdout <- Stdout() someVar as String? try someVar :=? knownFileName("unknown") stdout.println("TryWithGuard: the try body ran") FileExceptionExample().demonstrateNamedFileNotFound(someVar) catch -> ex as Exception stdout.println(`TryWithGuard: ${ex}`) stdout.println("TryWithGuard: carried on") TryWithAssignment() stdout <- Stdout() someVar as String? try someVar: knownFileName("report") stdout.println("TryWithAssignment: the try body ran") FileExceptionExample().demonstrateNamedFileNotFound(someVar) catch -> ex as Exception stdout.println(`TryWithAssignment: ${ex}`) TryWithDeclaration() stdout <- Stdout() try someVar <- knownFileName("unknown") stdout.println("TryWithDeclaration: the try body ran") FileExceptionExample().demonstrateNamedFileNotFound(someVar) catch -> ex as Exception stdout.println(`TryWithDeclaration: ${ex}`) //This is a 'different' 'someVar' - the previous one is now out of scope. someVar <- false require someVar? stdout.println("TryWithDeclaration: carried on")
Download exceptions_big_cats.ek9
Output
TryCatchExample
Exception: Lion: Exit Code: 1 Finished checking Lion Processing of Lion was NOT completed Would tidy up any state ready for program exit Too dangerous Tiger exit code 2 Finished checking Tiger Processing of Tiger was NOT completed Success with WildCat Finished checking WildCat Processing of WildCat was completed Leopard retry after 1971-02-01T14:00:00Z Finished checking Leopard Processing of Leopard was NOT completed Deferred until after 1971-02-01T14:00:00Z Lynx not processed Finished checking Lynx Processing of Lynx was NOT completed Deferred until after 1971-02-01T14:00:00Z Lynx not processed Finished checking Lynx Success with Lynx Finished checking Lynx Exception: File Not Found: MightNotExist.txt Automatically closed file if opened TryCatchExample: Exception: File Not Found: MightNotExist.txt
TryWithGuard
TryWithGuard: carried on
TryWithAssignment
TryWithAssignment: the try body ran TryWithAssignment: Exception: File Not Found: MightNotExist.txt
TryWithDeclaration
TryWithDeclaration: carried on