Created
April 29, 2019 17:51
-
-
Save antonyharfield/1928d02a1163cf115d701deca5b99f63 to your computer and use it in GitHub Desktop.
Revisions
-
antonyharfield created this gist
Apr 29, 2019 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,77 @@ // Result is a superpowered enum that can be Success or Failure // and the basis for a railway junction sealed class Result<T> data class Success<T>(val value: T): Result<T>() data class Failure<T>(val errorMessage: String): Result<T>() // Composition: apply a function f to Success results infix fun <T,U> Result<T>.then(f: (T) -> Result<U>) = when (this) { is Success -> f(this.value) is Failure -> Failure(this.errorMessage) } // Pipe input: the beginning of a railway infix fun <T,U> T.to(f: (T) -> Result<U>) = Success(this) then f // Handle error output: the end of a railway infix fun <T> Result<T>.otherwise(f: (String) -> Unit) = if (this is Failure) f(this.errorMessage) else Unit // An example email sending module that reads input, parses, validates, and sends fun main(args: Array<String>) { input() to ::parse then ::validate then ::send otherwise ::error } data class Email( val to: String, val subject: String, val body: String ) // Read in lines of input from the stdin fun readLines(prompts: List<String>): List<String> = prompts.map { print("${it}: ") readLine() ?: "" } fun input() = readLines(listOf("To", "Subject", "Body")) // Parse the lines of input to an Email object fun parse(inputs: List<String>): Result<Email> = if (inputs.size == 3) Success(Email(to = inputs[0], subject = inputs[1], body = inputs[2])) else Failure("Unexpected end of input") // Validate the email address fun validAddress(email: Email): Result<Email> = if (email.to.contains("@")) Success(email) else Failure("Invalid email address") // Validate the subject and body are not blank fun notBlank(email: Email): Result<Email> = if (email.subject != "" && email.body != "") Success(email) else Failure("Subject and body must not be blank") // Composition of validation functions fun validate(email: Email) = validAddress(email) then ::notBlank // Send the email (typically this would have an unhappy path too) fun send(email: Email): Result<Unit> { println("Sent to ${email.to}. Whoosh!") return Success(Unit) } // The error handler fun error(message: String) = println("Something went wrong: ${message}")