Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial examples with Sealed #44

Merged
merged 5 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 15 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ A repository to review the main concepts about Functional Programming with Java.
sdk env install
./mvnw clean test -DexcludedGroups=performance,endtoend
./mvnw clean test -DexcludedGroups=performance,endtoend -pl training
./mvnw clean test -DexcludedGroups=performance,endtoend -Dtest=LoomExamplesTest -pl training
./mvnw clean test -DexcludedGroups=performance,endtoend -Dtest=AgeProblemExampleTest -pl training
./mvnw clean test -Dgroups=performance
./mvnw clean test -Dgroups=endtoend

Expand All @@ -26,35 +26,14 @@ sdk env install

## Functional programming features in Java

- [x] Lambda Expressions (Functional interfaces, Functions, Supplier, Consumer & Predicates)
- [x] Optional
- [x] Stream API
- [x] CompletableFuture & Structural Concurrency
- [ ] Immutable Lists
- [ ] Sealed Classes
- [ ] Pattern Matching for Switch
- [x] Records & Record Patterns

## Functional programming timeline in Java

| Java Version | Feature | Date | Release notes |
|--------------|-------------------------------------------------------------------------------------|-----------|------------------------------------------------------------------------|
| Java 8 | - Lambda Expressions - Optional - Stream API - CompletableFuture | 18/3/2014 | https://www.oracle.com/java/technologies/javase/8-whats-new.html |
| Java 9 | - CompletableFuture updates | 21/9/2017 | https://www.oracle.com/java/technologies/javase/9-all-relnotes.html |
| Java 10 | - Optional updates - Immutable Lists | 20/3/2018 | https://www.oracle.com/java/technologies/javase/10-relnote-issues.html |
| Java 11 | - Not Predicate operator - Local-Variable Syntax for Lambda | 25/9/2018 | https://www.oracle.com/java/technologies/javase/11all-relnotes.html |
| Java 12 | - Teeing Collector - Pattern Matching | 19/3/2019 | https://www.oracle.com/java/technologies/javase/12-relnote-issues.html |
| Java 13 | - Switch Expressions enhancements | 17/9/2019 | https://www.oracle.com/java/technologies/javase/13-relnote-issues.html |
| Java 14 | - Records | 17/3/2020 | https://www.oracle.com/java/technologies/javase/14-relnote-issues.html |
| Java 15 | - Sealed Classes (Preview) | 15/9/2020 | https://www.oracle.com/java/technologies/javase/15-relnote-issues.html |
| Java 16 | - Sealed Classes (Preview) - Stream.toList | 16/3/2021 | https://www.oracle.com/java/technologies/javase/16-relnote-issues.html |
| Java 17 | - Sealed Classes (JEP 409) - Pattern Matching for Switch (JEP 406) (Preview) | 14/9/2021 | https://www.oracle.com/java/technologies/javase/17-relnote-issues.html |
| Java 18 | - Pattern Matching for switch (JEP 420) (Preview) | 22/3/2022 | https://www.oracle.com/java/technologies/javase/18all-relnotes.html |
| Java 19 | - Record Patterns - Pattern Matching for switch (JEP 427) (Preview) | 20/9/2022 | https://www.oracle.com/java/technologies/javase/19-relnote-issues.html |
| Java 20 | - Record Patterns (JEP 432) - Pattern Matching for Switch (JEP 433) (Preview) | 21/3/2023 | https://www.oracle.com/java/technologies/javase/20-relnote-issues.html |
| Java 21 | - Record Patterns (JEP 440) - Pattern Matching for switch (JEP 441) | 19/9/2023 | https://www.oracle.com/java/technologies/javase/21-relnote-issues.html |
| Java 22 | - Stream Gatherers (JEP 461) (Preview) | 19/3/2024 | https://www.oracle.com/java/technologies/javase/22-relnote-issues.html |

- [x] [Lambda Expressions](https://openjdk.org/jeps/126) (Functional interfaces, Functions, Supplier, Consumer & Predicates)
- [x] [Optional](https://openjdk.org/jeps/401)
- [x] [Stream API](https://openjdk.org/jeps/107) & [Gatherers](https://openjdk.org/jeps/461)
- [x] [CompletableFuture](https://openjdk.org/jeps/266) & [Structural Concurrency](https://openjdk.org/jeps/453)
- [x] [Immutable Lists](https://openjdk.org/jeps/269)
- [x] [Sealed Classes](https://openjdk.org/jeps/409)
- [x] [Pattern Matching for Switch](https://openjdk.org/jeps/441)
- [x] [Records](https://openjdk.org/jeps/395) & [Record Patterns](https://openjdk.org/jeps/440)

## How to run the presentation in local?

Expand All @@ -75,4 +54,9 @@ you can review the performance results:

- https://github.com/jabrena/latency-problems
- https://github.com/forax/loom-fiber
- https://github.com/forax/we_are_all_to_gather
- https://cr.openjdk.org/~vklang/Gatherers.html
- https://github.com/forax/we_are_all_to_gather
- https://www.infoq.com/articles/data-oriented-programming-java/
- https://inside.java/2024/05/23/dop-v1-1-introduction/
- https://inside.java/2024/05/27/dop-v1-1-immutable-transparent-data/
- https://inside.java/2024/05/29/dop-v1-1-model-data/
20 changes: 20 additions & 0 deletions docs/timeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Functional programming timeline in Java

| Java Version | Feature | Date | Release notes |
|--------------|-------------------------------------------------------------------------------------|-----------|------------------------------------------------------------------------|
| Java 8 | - Lambda Expressions - Optional - Stream API - CompletableFuture | 18/3/2014 | https://www.oracle.com/java/technologies/javase/8-whats-new.html |
| Java 9 | - CompletableFuture updates | 21/9/2017 | https://www.oracle.com/java/technologies/javase/9-all-relnotes.html |
| Java 10 | - Optional updates - Immutable Lists | 20/3/2018 | https://www.oracle.com/java/technologies/javase/10-relnote-issues.html |
| Java 11 | - Not Predicate operator - Local-Variable Syntax for Lambda | 25/9/2018 | https://www.oracle.com/java/technologies/javase/11all-relnotes.html |
| Java 12 | - Teeing Collector - Pattern Matching | 19/3/2019 | https://www.oracle.com/java/technologies/javase/12-relnote-issues.html |
| Java 13 | - Switch Expressions enhancements | 17/9/2019 | https://www.oracle.com/java/technologies/javase/13-relnote-issues.html |
| Java 14 | - Records | 17/3/2020 | https://www.oracle.com/java/technologies/javase/14-relnote-issues.html |
| Java 15 | - Sealed Classes (Preview) | 15/9/2020 | https://www.oracle.com/java/technologies/javase/15-relnote-issues.html |
| Java 16 | - Sealed Classes (Preview) - Stream.toList | 16/3/2021 | https://www.oracle.com/java/technologies/javase/16-relnote-issues.html |
| Java 17 | - Sealed Classes (JEP 409) - Pattern Matching for Switch (JEP 406) (Preview) | 14/9/2021 | https://www.oracle.com/java/technologies/javase/17-relnote-issues.html |
| Java 18 | - Pattern Matching for switch (JEP 420) (Preview) | 22/3/2022 | https://www.oracle.com/java/technologies/javase/18all-relnotes.html |
| Java 19 | - Record Patterns - Pattern Matching for switch (JEP 427) (Preview) | 20/9/2022 | https://www.oracle.com/java/technologies/javase/19-relnote-issues.html |
| Java 20 | - Record Patterns (JEP 432) - Pattern Matching for Switch (JEP 433) (Preview) | 21/3/2023 | https://www.oracle.com/java/technologies/javase/20-relnote-issues.html |
| Java 21 | - Record Patterns (JEP 440) - Pattern Matching for switch (JEP 441) | 19/9/2023 | https://www.oracle.com/java/technologies/javase/21-relnote-issues.html |
| Java 22 | - Stream Gatherers (JEP 461) (Preview) | 19/3/2024 | https://www.oracle.com/java/technologies/javase/22-relnote-issues.html |

22 changes: 22 additions & 0 deletions training/src/main/java/info/jab/fp/others/FibonacciCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package info.jab.fp.others;

import java.util.function.Function;

public class FibonacciCalculator {
public static void main(String[] args) {
// Define the recursive Fibonacci function using the Function interface
Function<Integer, Integer> fib = new Function<>() {
@Override
public Integer apply(Integer n) {
if (n <= 1) return n;
// Recursive call using this function reference
return this.apply(n - 1) + this.apply(n - 2);
}
};

// Calculate Fibonacci numbers
int number = 10; // Example number
int result = fib.apply(number);
System.out.println("Fibonacci number for " + number + " is " + result);
}
}
40 changes: 40 additions & 0 deletions training/src/main/java/info/jab/fp/sealed/AgeProblemExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package info.jab.fp.sealed;

public class AgeProblemExample {

record Age(Integer age) {}

// these are the potential problems
sealed interface AgeProblem permits InvalidAge, NotLegalAdult {
String getMessage();
}

record InvalidAge() implements AgeProblem {

@Override
public String getMessage() {
return "Hello";
}
}

record NotLegalAdult() implements AgeProblem {

@Override
public String getMessage() {
return "World";
}
}

//Either<AgeProblem, Age> problem1 = Either.left(new InvalidAge());
//Either<AgeProblem, Age> problem2 = Either.left(new NotLegalAdult());
//Either<AgeProblem, Age> success = Either.right(new Age(20));

public Either<AgeProblem, Age> validAdultAge(Integer age) {
return switch (age) {
case Integer a when a < 0 -> Either.left(new InvalidAge());
case Integer a when a < 18 -> Either.left(new NotLegalAdult());
default -> Either.right(new Age(age));
};
}

}
57 changes: 57 additions & 0 deletions training/src/main/java/info/jab/fp/sealed/Either.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package info.jab.fp.sealed;

// Either.java
public sealed interface Either<L, R> permits Either.Left, Either.Right {

<T> T fold(java.util.function.Function<? super L, ? extends T> leftMapper,
java.util.function.Function<? super R, ? extends T> rightMapper);

boolean isLeft();
boolean isRight();

static <L, R> Either<L, R> left(L value) {
return new Left<>(value);
}

static <L, R> Either<L, R> right(R value) {
return new Right<>(value);
}

record Left<L, R>(L value) implements Either<L, R> {

@Override
public <T> T fold(java.util.function.Function<? super L, ? extends T> leftMapper,
java.util.function.Function<? super R, ? extends T> rightMapper) {
return leftMapper.apply(value);
}

@Override
public boolean isLeft() {
return true;
}

@Override
public boolean isRight() {
return false;
}
}

record Right<L, R>(R value) implements Either<L, R> {

@Override
public <T> T fold(java.util.function.Function<? super L, ? extends T> leftMapper,
java.util.function.Function<? super R, ? extends T> rightMapper) {
return rightMapper.apply(value);
}

@Override
public boolean isLeft() {
return false;
}

@Override
public boolean isRight() {
return true;
}
}
}
19 changes: 19 additions & 0 deletions training/src/main/java/info/jab/fp/sealed/EitherExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package info.jab.fp.sealed;

import java.util.function.Function;

public class EitherExample {
public static void main(String[] args) {
Either<String, Integer> success = Either.right(42);
Either<String, Integer> failure = Either.left("Error occurred");

Function<Either<String, Integer>, String> resultMapper = either ->
either.fold(
error -> "Failed with error: " + error,
value -> "Success with value: " + value
);

System.out.println(resultMapper.apply(success)); // Output: Success with value: 42
System.out.println(resultMapper.apply(failure)); // Output: Failed with error: Error occurred
}
}
18 changes: 18 additions & 0 deletions training/src/main/java/info/jab/fp/sealed/EnumExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package info.jab.fp.sealed;

public class EnumExample {

public static void main() {

enum Planet { MERCURY, VENUS, EARTH }

Planet p = Planet.EARTH;

switch (p) {
case MERCURY -> System.out.println("Mercury");
case VENUS -> System.out.println("Venus");
case EARTH -> System.out.println("Earth");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package info.jab.fp.sealed;

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import info.jab.fp.sealed.AgeProblemExample.Age;
import info.jab.fp.sealed.AgeProblemExample.AgeProblem;
import info.jab.fp.sealed.AgeProblemExample.InvalidAge;
import info.jab.fp.sealed.AgeProblemExample.NotLegalAdult;
import info.jab.utils.TestLoggerExtension;

@ExtendWith(TestLoggerExtension.class)
public class AgeProblemExampleTest {

@Test
public void should_notValidate() {

AgeProblemExample example = new AgeProblemExample();

var result = example.validAdultAge(-1);

assertThat(result).isInstanceOf(Either.class);
assertThat(result.isLeft()).isTrue();

var result2 = example.validAdultAge(17);
assertThat(result2).isInstanceOf(Either.class);
assertThat(result2.isLeft()).isTrue();

var obj = result2.fold(left -> left, right -> right);
if (obj instanceof AgeProblem) {
AgeProblem ageProblem = (AgeProblem) obj;
if (ageProblem instanceof InvalidAge) {
InvalidAge invalidAge = (InvalidAge) ageProblem;
System.out.println("Handled InvalidAge: " + invalidAge.getMessage());
} else if (ageProblem instanceof NotLegalAdult) {
NotLegalAdult notLegalAdult = (NotLegalAdult) ageProblem;
System.out.println("Handled NotLegalAdult: " + notLegalAdult.getMessage());
}
}
}

@Test
public void should_validate() {

AgeProblemExample example = new AgeProblemExample();

var result = example.validAdultAge(18);

assertThat(result).isInstanceOf(Either.class);
assertThat(result.isRight()).isTrue();

var obj = result.fold(left -> left, right -> right);
if (obj instanceof Age) {
Age age = (Age) obj;
System.out.println("Handled Age: " + age);
}
}

}
13 changes: 13 additions & 0 deletions training/src/test/java/info/jab/fp/sealed/EitherTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package info.jab.fp.sealed;

import org.junit.jupiter.api.Test;

public class EitherTest {

@Test
public void testFold() {
Either<String, Integer> success = Either.right(42);
Either<String, Integer> failure = Either.left("Error occurred");
}

}