README.org: adjust headings

This commit is contained in:
Paul Campbell 2018-07-16 18:52:33 +01:00
parent e5958ba432
commit c7c7c6ebeb

View file

@ -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<String> {
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<String> {
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<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
TypeAlias<String> 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<Integer, String> validMessage() {
return v -> String.format("Value %d is even", v);
}
private static Function<Integer, String> validMessage() {
return v -> String.format("Value %d is even", v);
}
private static Predicate<Integer> isEven() {
return v -> v % 2 == 0;
}
private static Predicate<Integer> 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<String> doSomething(final String state) {
return Result.of(() -> state + ", it's all good.");
}
private Result<String> 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