diff --git a/README.org b/README.org index 411f3e0..c882f76 100644 --- a/README.org +++ b/README.org @@ -39,150 +39,146 @@ The latest version should be shown above with the nexus and maven-central badges or can be found on [[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][Maven Central]]. -** Usage + +** TypeAlias :PROPERTIES: - :CUSTOM_ID: usage + :CUSTOM_ID: typealias :END: -*** TypeAlias - :PROPERTIES: - :CUSTOM_ID: typealias - :END: + In Haskell it is possible to create an alias for a Type, and to then use + that alias with the same behaviour as the original, except that the compiler + doesn't treat the alias as the same Type and will generate compiler errors + if you try and use them together. e.g.: - In Haskell it is possible to create an alias for a Type, and to then use - that alias with the same behaviour as the original, except that the compiler - doesn't treat the alias as the same Type and will generate compiler errors - if you try and use them together. e.g.: + #+BEGIN_SRC haskell + type PhoneNumber = String + type Name = String + type PhoneBook = [(Name,PhoneNumber)] + #+END_SRC - #+BEGIN_SRC haskell - type PhoneNumber = String - type Name = String - type PhoneBook = [(Name,PhoneNumber)] - #+END_SRC + In Java we don't have the ability to have that true alias, so TypeAlias is + more of a type-wrapper. It's as close as I could get to a Haskell type alias + in Java. - In Java we don't have the ability to have that true alias, so TypeAlias is - more of a type-wrapper. It's as close as I could get to a Haskell type alias - in Java. + The benefits of using TypeAlias are: - The benefits of using TypeAlias are: + - encapsulation of the wrapped type when passing references through code + that doesn't need to access the actual value, but only to pass it on + - type-safe parameters where you would otherwise be passing Strings, + Integers, Lists, or other general classes + - equality and hashcode + - less verbose than implementing your own - - encapsulation of the wrapped type when passing references through code - that doesn't need to access the actual value, but only to pass it on - - type-safe parameters where you would otherwise be passing Strings, - Integers, Lists, or other general classes - - equality and hashcode - - less verbose than implementing your own + *TypeAlias Example:* - *TypeAlias Example:* + #+BEGIN_SRC java + class PhoneNumber extends TypeAlias { + private PhoneNumber(final String value) { + super(value); + } + public static PhoneNumber of(final String phoneNumber) { + return new PhoneNumber(phoneNumber); + } + } + #+END_SRC - #+BEGIN_SRC java - class PhoneNumber extends TypeAlias { - private PhoneNumber(final String value) { - super(value); - } - public static PhoneNumber of(final String phoneNumber) { - return new PhoneNumber(phoneNumber); - } - } - #+END_SRC + *Roll your own:* - *Roll your own:* + #+BEGIN_SRC java + class PhoneNumber { + private final String value; + private PhoneNumber(final String value) { + this.value = value; + } + public static PhoneNumber of(final String phoneNumber) { + return new PhoneNumber(phoneNumber); + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PhoneNumber that = (PhoneNumber) o; + return Objects.equals(value, that.value); + } + @Override + public int hashCode() { + return Objects.hash(value); + } + public String getValue() { + return value; + } + } + #+END_SRC - #+BEGIN_SRC java - class PhoneNumber { - private final String value; - private PhoneNumber(final String value) { - this.value = value; - } - public static PhoneNumber of(final String phoneNumber) { - return new PhoneNumber(phoneNumber); - } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PhoneNumber that = (PhoneNumber) o; - return Objects.equals(value, that.value); - } - @Override - public int hashCode() { - return Objects.hash(value); - } - public String getValue() { - return value; - } - } - #+END_SRC + *Lombok:* - *Lombok:* + Although, if you are using Lombok, that can be equally terse, both it and + TypeAlias coming in at 8 lines each, compared to 24 for rolling your + own: - Although, if you are using Lombok, that can be equally terse, both it and - TypeAlias coming in at 8 lines each, compared to 24 for rolling your - own: + #+BEGIN_SRC java + @Value + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + class PhoneNumber { + private final String value; + public static PhoneNumber of(final String phoneNumber) { + return new PhoneNumber(phoneNumber); + } + } + #+END_SRC - #+BEGIN_SRC java - @Value - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - class PhoneNumber { - private final String value; - public static PhoneNumber of(final String phoneNumber) { - return new PhoneNumber(phoneNumber); - } - } - #+END_SRC +** Maybe + :PROPERTIES: + :CUSTOM_ID: maybe + :END: -*** Maybe - :PROPERTIES: - :CUSTOM_ID: maybe - :END: + Allows specifying that a value may or may not be present. Similar to + =Optional=. =Maybe= provides additional methods that =Optional= doesn't: + =isNothing()=, =stream()=, =ifNothing()= and =match()=. =Maybe= does not + have a =get()= method. - Allows specifying that a value may or may not be present. Similar to - =Optional=. =Maybe= provides additional methods that =Optional= doesn't: - =isNothing()=, =stream()=, =ifNothing()= and =match()=. =Maybe= does not - have a =get()= method. + Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will + continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavi.io + follows the same behaviour as =Maybe=]]. - Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will - continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavi.io - follows the same behaviour as =Maybe=]]. + #+BEGIN_SRC java + import net.kemitix.mon.maybe.Maybe; - #+BEGIN_SRC java - import net.kemitix.mon.maybe.Maybe; + import java.util.function.Function; + import java.util.function.Predicate; - import java.util.function.Function; - import java.util.function.Predicate; + class MaybeExample { - class MaybeExample { + public static void main(String[] args) { + Maybe.just(countArgs(args)) + .filter(isEven()) + .map(validMessage()) + .match( + just -> System.out.println(just), + () -> System.out.println("Not an valid value") + ); + } - public static void main(String[] args) { - Maybe.just(countArgs(args)) - .filter(isEven()) - .map(validMessage()) - .match( - just -> System.out.println(just), - () -> System.out.println("Not an valid value") - ); - } + private static Function validMessage() { + return v -> String.format("Value %d is even", v); + } - private static Function validMessage() { - return v -> String.format("Value %d is even", v); - } + private static Predicate isEven() { + return v -> v % 2 == 0; + } - private static Predicate isEven() { - return v -> v % 2 == 0; - } + private static Integer countArgs(String[] args) { + return args.length; + } + } + #+END_SRC - private static Integer countArgs(String[] args) { - return args.length; - } - } - #+END_SRC - - In the above example, the number of command line arguments are counted, if - there are an even number of them then a message is created and printed by - the Consumer parameter in the =match= call. If there is an odd number of - arguments, then the filter will return =Maybe.nothing()=, meaning that the - =nothing= drops straight through the map and triggers the Runnable parameter - in the =match= call. + In the above example, the number of command line arguments are counted, if + there are an even number of them then a message is created and printed by + the Consumer parameter in the =match= call. If there is an odd number of + arguments, then the filter will return =Maybe.nothing()=, meaning that the + =nothing= drops straight through the map and triggers the Runnable parameter + in the =match= call. **** =Maybe= is a Monad: @@ -392,60 +388,60 @@ .toOptional(); #+END_SRC -*** Result - :PROPERTIES: - :CUSTOM_ID: result - :END: +** Result + :PROPERTIES: + :CUSTOM_ID: result + :END: - Allows handling error conditions without the need to catch exceptions. + Allows handling error conditions without the need to catch exceptions. - When a =Result= is returned from a method it will contain one of two values. - Either the actual result, or an error in the form of an =Exception=. The - exception is returned within the =Result= and is not thrown. + When a =Result= is returned from a method it will contain one of two values. + Either the actual result, or an error in the form of an =Exception=. The + exception is returned within the =Result= and is not thrown. - #+BEGIN_SRC java - import net.kemitix.mon.result.Result; + #+BEGIN_SRC java + import net.kemitix.mon.result.Result; - import java.io.IOException; + import java.io.IOException; - class ResultExample implements Runnable { + class ResultExample implements Runnable { - public static void main(final String[] args) { - new ResultExample().run(); - } + public static void main(final String[] args) { + new ResultExample().run(); + } - @Override - public void run() { - Result.of(() -> callRiskyMethod()) - .flatMap(state -> doSomething(state)) - .match( - success -> System.out.println(success), - error -> error.printStackTrace() - ); - } + @Override + public void run() { + Result.of(() -> callRiskyMethod()) + .flatMap(state -> doSomething(state)) + .match( + success -> System.out.println(success), + error -> error.printStackTrace() + ); + } - private String callRiskyMethod() throws IOException { - return "I'm fine"; - } + private String callRiskyMethod() throws IOException { + return "I'm fine"; + } - private Result doSomething(final String state) { - return Result.of(() -> state + ", it's all good."); - } + private Result doSomething(final String state) { + return Result.of(() -> state + ", it's all good."); + } - } - #+END_SRC + } + #+END_SRC - In the above example the string ="I'm fine"= is returned by - =callRiskyMethod()= within a successful =Result=. The =.flatMap()= call, - unwraps that =Result= and, as it is a success, passes the contents to - =doSomething()=, which in turn returns a =Result= that the =.flatMap()= call - returns. =match()= is called on the =Result= and, being a success, will call - the success =Consumer=. + In the above example the string ="I'm fine"= is returned by + =callRiskyMethod()= within a successful =Result=. The =.flatMap()= call, + unwraps that =Result= and, as it is a success, passes the contents to + =doSomething()=, which in turn returns a =Result= that the =.flatMap()= call + returns. =match()= is called on the =Result= and, being a success, will call + the success =Consumer=. - Had =callRiskyMethod()= thrown an exception it would have been caught by the - =Result.of()= method which would have then been an error =Result=. An error - Result would have ignored the =flatMap= and skipped to the =match()= when it - would have called the error =Consumer=. + Had =callRiskyMethod()= thrown an exception it would have been caught by the + =Result.of()= method which would have then been an error =Result=. An error + Result would have ignored the =flatMap= and skipped to the =match()= when it + would have called the error =Consumer=. **** =Result= is a Monad