Skip to content

Commit

Permalink
Minor fixes, Readme update
Browse files Browse the repository at this point in the history
  • Loading branch information
xyzsd committed Jan 12, 2024
1 parent 05a684d commit 53e6b5e
Show file tree
Hide file tree
Showing 9 changed files with 336 additions and 145 deletions.
86 changes: 77 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,87 @@
[![Java CI with Gradle](https://github.com/xyzsd/dichotomy/actions/workflows/gradle.yml/badge.svg)](https://github.com/xyzsd/dichotomy/actions/workflows/gradle.yml)

# dichotomy
Either and Result monadic types for Java.
Includes specialized types Try and Maybe.
Sealed monads for Java.

All types are sealed, and can be used in switch statements and
Generally these types are used to return one of two values, such as success or failure.

All types are sealed (Sum types), and can be used in `switch` expressions and with
pattern matching.

## UPDATE (18-Dec-2023):
* Substantially restructured and improved for the pending 1.0 version
* Markedly improved Try type, added Maybe type
* Tests near-complete
* Improved documentation
* still needs: a nice intro (with synopsis and illustrative examples)
### `Either`:
An general immutable type that can only be *either* one of two types.
The types are called `Left<L>` and `Right<R>`. By convention, the Left type
indicates failure, while the Right type indicates success.

### `Result`:
Similar to an `Either`, but with success/failure semantics more clearly defined.
An `OK<V>` Result indicates success, and an `Err<E>` Result indicates failure. Failure
types do not need to be Exceptions.

```java

Result<Double,String> result = Result.<Integer, String>ofOK(3828) // returns an OK<Integer>
.map(x -> x*10.0) // map to Result<Double,String>, after multiplying x 10
.match(System.out::println) // print "38280.0" to console
.matchErr(System.err::println); // ignored, as this is an OK

switch(result) {
case OK<Double,String> ok -> System.out.println("value ok! value: "+ok.value());
case Err<Double,String> err -> System.err.println(err.value());
}

// JDK 21+
switch(result) {
case OK(Double x) when x > 0 -> System.out.println("positive");
case OK(Double x) -> System.out.println("0 or negative");
case Err(String s) -> System.err.println(s);
}

// anotherResult here will be an Err<String>
Result<Double,String> anotherResult = Result.<Integer, String>ofErr("Insufficient entropy")
.map(x -> x*10.0 ) // ignored, as this is an Err
.match(System.out::println) // ignored, as this is an Err
.matchErr(System.err::println); // "Insufficient entropy" printed to System.err
```


### `Try`:
A specialized type of `Result`. A `Try` wraps a function or block; if
successful, a `Success` Try is returned; otherwise, a `Failure` Try containing
an Exception is returned. Intermediate operations which return Trys will also
catch generated Exceptions.

```java

final Try<Integer> result = Try.ofSuccess( 777 )
.map( i -> i * 1000 ) // results in a Try<Integer> with a value of 777000
.exec( System.out::println ) // prints "777000"
.map( i -> i / 0 ) // the ArithmeticException is caught as a Try.Failure
.exec( System.out::println ); // does not exec() because we are a Failure

// prints "ERROR: java.lang.ArithmeticException: / by zero"
switch(result) {
case Success(Integer i) -> System.out.printf("Operation completed successfully. Value: %d\n", i);
case Failure(Throwable t) -> System.err.printf("ERROR: %s\n", t);
}


```

### `Maybe`:
Analogous to the JDK `Optional` type, but sealed so it may be used in `switch`
statements and with pattern matching.


## Updates (January 2024)
- [x] Refactored, now with substantial improvements
- [x] New Try class
- [x] New Maybe class (analogous to Optional)
- [x] Near-complete test coverage
- [x] Improved documentation
- [x] Targets JDK 21
- [ ] Examples (still in progress)
- [ ] Maven release

Download
--------
Expand Down
13 changes: 5 additions & 8 deletions src/main/java/net/xyzsd/dichotomy/Result.java
Original file line number Diff line number Diff line change
Expand Up @@ -1182,13 +1182,10 @@ public boolean containsErr(@Nullable E errValue) {

@Override
public @NotNull V expect() throws RuntimeException {
// TODO: when pattern-switch is out of preview, convert this code
if (value instanceof RuntimeException e) {
throw e;
} else if (value instanceof Throwable t) {
throw new NoSuchElementException( t );
} else {
throw new NoSuchElementException( String.valueOf( value ) );
switch(value) {
case RuntimeException e -> throw e;
case Throwable t -> throw new NoSuchElementException(t);
default -> throw new NoSuchElementException( String.valueOf( value ) );
}
}

Expand All @@ -1198,7 +1195,7 @@ public boolean containsErr(@Nullable E errValue) {
throw requireNonNull( exFn.apply( value ) );
}

// For types where the value type is unchanged and exists, but the generic type of the value differs
// For types where the value type is unchanged and exists, but the generic type of the value differs,
// just cast and return. Types are erased so there is no need to create a new object.
// The Error stays the same; only the empty value signature changes
@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,9 @@ public interface EitherCollectors {


static private <L, R> void add(Accumulator<R, L> listBox, Either<L, R> either) {
// TODO: use switch/case when we support JDK > 20
if(either instanceof Either.Right<L,R> right) {
listBox.okList.add( right.value() );
} else if(either instanceof Either.Left<L,R> left) {
listBox.errList.add( left.value() );
} else {
throw new IllegalStateException();
switch(either) {
case Either.Right(R r) -> listBox.okList.add( r );
case Either.Left(L l) -> listBox.errList.add( l );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,9 @@ public interface ResultCollectors {


static private <OK, ERR> void add(Accumulator<OK, ERR> listBox, Result<OK, ERR> result) {
// TODO: use switch/case when we support JDK > 20
if(result instanceof Result.OK<OK, ERR> ok) {
listBox.okList.add( ok.value() );
} else if(result instanceof Result.Err<OK, ERR> err) {
listBox.errList.add( err.value() );
} else {
throw new IllegalStateException();
switch(result) {
case Result.OK(OK v) -> listBox.okList.add( v );
case Result.Err(ERR e) -> listBox.errList.add( e );
}
}

Expand Down
Loading

0 comments on commit 53e6b5e

Please sign in to comment.