README.org: adjust headings
This commit is contained in:
parent
e5958ba432
commit
c7c7c6ebeb
1 changed files with 157 additions and 161 deletions
318
README.org
318
README.org
|
@ -39,150 +39,146 @@
|
||||||
The latest version should be shown above with the nexus and maven-central
|
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]].
|
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:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: usage
|
:CUSTOM_ID: typealias
|
||||||
:END:
|
:END:
|
||||||
|
|
||||||
*** TypeAlias
|
In Haskell it is possible to create an alias for a Type, and to then use
|
||||||
:PROPERTIES:
|
that alias with the same behaviour as the original, except that the compiler
|
||||||
:CUSTOM_ID: typealias
|
doesn't treat the alias as the same Type and will generate compiler errors
|
||||||
:END:
|
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
|
#+BEGIN_SRC haskell
|
||||||
that alias with the same behaviour as the original, except that the compiler
|
type PhoneNumber = String
|
||||||
doesn't treat the alias as the same Type and will generate compiler errors
|
type Name = String
|
||||||
if you try and use them together. e.g.:
|
type PhoneBook = [(Name,PhoneNumber)]
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC haskell
|
In Java we don't have the ability to have that true alias, so TypeAlias is
|
||||||
type PhoneNumber = String
|
more of a type-wrapper. It's as close as I could get to a Haskell type alias
|
||||||
type Name = String
|
in Java.
|
||||||
type PhoneBook = [(Name,PhoneNumber)]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
In Java we don't have the ability to have that true alias, so TypeAlias is
|
The benefits of using TypeAlias are:
|
||||||
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:
|
- 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
|
*TypeAlias Example:*
|
||||||
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:*
|
#+BEGIN_SRC java
|
||||||
|
class PhoneNumber extends TypeAlias<String> {
|
||||||
|
private PhoneNumber(final String value) {
|
||||||
|
super(value);
|
||||||
|
}
|
||||||
|
public static PhoneNumber of(final String phoneNumber) {
|
||||||
|
return new PhoneNumber(phoneNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
#+BEGIN_SRC java
|
*Roll your own:*
|
||||||
class PhoneNumber extends TypeAlias<String> {
|
|
||||||
private PhoneNumber(final String value) {
|
|
||||||
super(value);
|
|
||||||
}
|
|
||||||
public static PhoneNumber of(final String phoneNumber) {
|
|
||||||
return new PhoneNumber(phoneNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
*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
|
*Lombok:*
|
||||||
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:*
|
Although, if you are using Lombok, that can be equally terse, both it and
|
||||||
|
TypeAlias<String> 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
|
#+BEGIN_SRC java
|
||||||
TypeAlias<String> coming in at 8 lines each, compared to 24 for rolling your
|
@Value
|
||||||
own:
|
@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
|
** Maybe
|
||||||
@Value
|
:PROPERTIES:
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
:CUSTOM_ID: maybe
|
||||||
class PhoneNumber {
|
:END:
|
||||||
private final String value;
|
|
||||||
public static PhoneNumber of(final String phoneNumber) {
|
|
||||||
return new PhoneNumber(phoneNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
*** Maybe
|
Allows specifying that a value may or may not be present. Similar to
|
||||||
:PROPERTIES:
|
=Optional=. =Maybe= provides additional methods that =Optional= doesn't:
|
||||||
:CUSTOM_ID: maybe
|
=isNothing()=, =stream()=, =ifNothing()= and =match()=. =Maybe= does not
|
||||||
:END:
|
have a =get()= method.
|
||||||
|
|
||||||
Allows specifying that a value may or may not be present. Similar to
|
Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will
|
||||||
=Optional=. =Maybe= provides additional methods that =Optional= doesn't:
|
continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavi.io
|
||||||
=isNothing()=, =stream()=, =ifNothing()= and =match()=. =Maybe= does not
|
follows the same behaviour as =Maybe=]].
|
||||||
have a =get()= method.
|
|
||||||
|
|
||||||
Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will
|
#+BEGIN_SRC java
|
||||||
continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavi.io
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
follows the same behaviour as =Maybe=]].
|
|
||||||
|
|
||||||
#+BEGIN_SRC java
|
import java.util.function.Function;
|
||||||
import net.kemitix.mon.maybe.Maybe;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import java.util.function.Function;
|
class MaybeExample {
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
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) {
|
private static Function<Integer, String> validMessage() {
|
||||||
Maybe.just(countArgs(args))
|
return v -> String.format("Value %d is even", v);
|
||||||
.filter(isEven())
|
}
|
||||||
.map(validMessage())
|
|
||||||
.match(
|
|
||||||
just -> System.out.println(just),
|
|
||||||
() -> System.out.println("Not an valid value")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Function<Integer, String> validMessage() {
|
private static Predicate<Integer> isEven() {
|
||||||
return v -> String.format("Value %d is even", v);
|
return v -> v % 2 == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Predicate<Integer> isEven() {
|
private static Integer countArgs(String[] args) {
|
||||||
return v -> v % 2 == 0;
|
return args.length;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
private static Integer countArgs(String[] args) {
|
In the above example, the number of command line arguments are counted, if
|
||||||
return args.length;
|
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
|
||||||
#+END_SRC
|
=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:
|
**** =Maybe= is a Monad:
|
||||||
|
|
||||||
|
@ -392,60 +388,60 @@
|
||||||
.toOptional();
|
.toOptional();
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
*** Result
|
** Result
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:CUSTOM_ID: result
|
:CUSTOM_ID: result
|
||||||
:END:
|
: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.
|
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
|
Either the actual result, or an error in the form of an =Exception=. The
|
||||||
exception is returned within the =Result= and is not thrown.
|
exception is returned within the =Result= and is not thrown.
|
||||||
|
|
||||||
#+BEGIN_SRC java
|
#+BEGIN_SRC java
|
||||||
import net.kemitix.mon.result.Result;
|
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) {
|
public static void main(final String[] args) {
|
||||||
new ResultExample().run();
|
new ResultExample().run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Result.of(() -> callRiskyMethod())
|
Result.of(() -> callRiskyMethod())
|
||||||
.flatMap(state -> doSomething(state))
|
.flatMap(state -> doSomething(state))
|
||||||
.match(
|
.match(
|
||||||
success -> System.out.println(success),
|
success -> System.out.println(success),
|
||||||
error -> error.printStackTrace()
|
error -> error.printStackTrace()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String callRiskyMethod() throws IOException {
|
private String callRiskyMethod() throws IOException {
|
||||||
return "I'm fine";
|
return "I'm fine";
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<String> doSomething(final String state) {
|
private Result<String> doSomething(final String state) {
|
||||||
return Result.of(() -> state + ", it's all good.");
|
return Result.of(() -> state + ", it's all good.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
In the above example the string ="I'm fine"= is returned by
|
In the above example the string ="I'm fine"= is returned by
|
||||||
=callRiskyMethod()= within a successful =Result=. The =.flatMap()= call,
|
=callRiskyMethod()= within a successful =Result=. The =.flatMap()= call,
|
||||||
unwraps that =Result= and, as it is a success, passes the contents to
|
unwraps that =Result= and, as it is a success, passes the contents to
|
||||||
=doSomething()=, which in turn returns a =Result= that the =.flatMap()= call
|
=doSomething()=, which in turn returns a =Result= that the =.flatMap()= call
|
||||||
returns. =match()= is called on the =Result= and, being a success, will call
|
returns. =match()= is called on the =Result= and, being a success, will call
|
||||||
the success =Consumer=.
|
the success =Consumer=.
|
||||||
|
|
||||||
Had =callRiskyMethod()= thrown an exception it would have been caught by the
|
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.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
|
Result would have ignored the =flatMap= and skipped to the =match()= when it
|
||||||
would have called the error =Consumer=.
|
would have called the error =Consumer=.
|
||||||
|
|
||||||
**** =Result= is a Monad
|
**** =Result= is a Monad
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue