Skip to content
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
## CHANGELOG

## [Unreleased]
- Future notes.
### Features
- Overhaul list zipping: make Cons(head, tail) less lazy, preserving laziness only for infinite sublists.
### Docs
- Documentation: Complete 16-week v2.0 production readiness plan (docs/PLAN.md) with detailed weekly breakdowns, parallel work streams, risk mitigation, and success metrics
- Documentation: Update README.md to properly link and describe the v2.0 production readiness plan
Expand All @@ -24,9 +27,9 @@
- Documentation: Add cross-references between BREAKING_CHANGES.md and API_AUDIT_v2.0.md for improved navigation
- Documentation: Update README.md with v2.0 release planning section and migration guides
- Documentation: Remove registry-related items from the v2.0 plan to align with annotation-processor-driven code generation.
- Documentation: Add initial documentation for core concepts in docs/CORE_CONCEPTS.md, covering functional and algebraic structures, annotations, type hierarchy, and minimal implementations.
### Infrastructure
- Pipeline compiles in java 17, 21 and 24
- Future notes.
### Code smells
- Some sonar code smells removed by refactoring and code cleanup, but many remain due to the nature of the generated code and reflection utilities. Focus is on critical issues and maintainable code rather than eliminating all smells.

Expand Down
19 changes: 3 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,9 @@ Welcome to the complete documentation for **Functional Java by Annotations**. Th

**Understanding the library**

- **[Core Concepts](docs/CORE_CONCEPTS.md)**
- Type hierarchy and structure
- Functional vs Algebraic structures
- Minimal implementations
- Type constructors and variance

- **[Functional Structures](docs/FUNCTIONAL_STRUCTURES.md)**
- Functor, Applicative, Monad
- Foldable, Traversal
- Alternative
- Laws and requirements

- **[Algebraic Structures](docs/ALGEBRAIC_STRUCTURES.md)**
- Semigroup, Monoid
- Ring
- Laws and examples
- **[Core Concepts](docs/CORE_CONCEPTS.md)**: Overview of functional and algebraic structures, annotations, type hierarchy, and minimal implementations.
- **[Functional Structures](docs/FUNCTIONAL_STRUCTURES.md)**: Details on Functor, Applicative, Monad, Foldable, Traversal, Alternative, and their laws.
- **[Algebraic Structures](docs/ALGEBRAIC_STRUCTURES.md)**: Details on Semigroup, Monoid, Ring, and their laws.

### 📖 Practical Guides

Expand Down
37 changes: 37 additions & 0 deletions docs/CORE_CONCEPTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Core Concepts

## Introduction
This project leverages Java annotations to define and implement functional and algebraic structures. It enables a declarative approach to programming, making code more modular, composable, and easier to reason about.

## Functional Structures
Functional structures are abstractions that allow operations on data in a context. Common examples include:
- **Functor**: Enables mapping functions over values in a context.
- **Applicative**: Allows applying functions wrapped in a context to values in a context.
- **Monad**: Supports chaining computations in a context.
- **Foldable**: Reduces structures to a single value.
- **Traversal**: Maps with effects across structures.
- **Alternative**: Provides choice operations.

## Algebraic Structures
Algebraic structures define mathematical operations and laws:
- **Semigroup**: Associative operation (`op`).
- **Monoid**: Semigroup with an identity element (`unit`).
- **Ring**: Supports addition and multiplication operations.

## Annotations
Annotations such as `@Functor`, `@Monad`, `@Semigroup`, etc., are used to mark types and specify minimal required methods. The annotation processor generates boilerplate code and validates that the required methods are present.

## Type Hierarchy and Structure
Types are organized hierarchically, with interfaces like `Algebraic` and `Structure` serving as base types. Each structure requires minimal implementations (e.g., `map` for Functor, `op` for Semigroup).

## Minimal Implementations
Each annotation specifies the minimal set of methods required. For example:
- `@Functor`: Requires `map`.
- `@Monoid`: Requires `op` and `unit`.
- `@Ring`: Requires both addition and multiplication operations.

## Type Constructors and Variance
The library supports generic types and type constructors, enabling flexible and reusable abstractions.

---
For more details, see [Functional Structures](FUNCTIONAL_STRUCTURES.md) and [Algebraic Structures](ALGEBRAIC_STRUCTURES.md).
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ public <C> C either(Function<A, C> aToC, Function<B, C> bToC) {
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != getClass()) return false;
return Objects.equals(a, ((Left<A, B>) obj).a);
if (obj == null) return false;
if (obj instanceof Left<?,?> left) {
return Objects.equals(a, left.a);
} else {
return false;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.dan323.functional.data.list;

import com.dan323.functional.data.optional.Maybe;

import java.util.function.BiFunction;
import java.util.function.Function;

/**
Expand All @@ -23,8 +22,8 @@ final class Cons<A> extends InfiniteList<A> {
}

@Override
public Maybe<A> head() {
return Maybe.of(head);
public A getHead() {
return head;
}

@Override
Expand All @@ -37,6 +36,11 @@ public <B> InfiniteList<B> map(Function<A, B> mapping) {
return new Cons<>(mapping.apply(head), tail().map(mapping));
}

public <B,C> InfiniteList<C> zipBy(BiFunction<A, B, C> mapper, InfiniteList<B> list) {
var newHead = mapper.apply(head,list.getHead());
return new Cons<>(newHead, (InfiniteList<C>) ZipApplicative.liftA2(mapper, tail, list.tail()));
}

Comment thread
dan323 marked this conversation as resolved.
@Override
public String toString() {
return "[" + head + "," + tail + "]";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.dan323.functional.data.list;

import java.util.function.Function;

public final class Cycle<A> extends InfiniteList<A> {

private final FiniteList<A> cycled;

Cycle(FiniteList<A> cycle) {
if (cycle == null || cycle.length() == 0) {
throw new IllegalArgumentException("The list to be cycled must not be null or empty.");
}
this.cycled = cycle;
}

@Override
public A getHead() {
return cycled.head().maybe(Function.identity(), null);
}

@Override
public InfiniteList<A> tail() {
var init = cycled.tail();
return ListUtils.concat(init, this);
}

@Override
public <B> Cycle<B> map(Function<A, B> mapping) {
return new Cycle<>(cycled.map(mapping));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ static <A> FiniteList<A> of(A... a) {
}
}

@Override
default FiniteList<A> cons(A head) {
return cons(head, this);
}

@SafeVarargs
private static <A> FiniteList<A> of(int n, A... a) {
if (n >= a.length) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static <A> AlternativeMonoid<FiniteList<A>, FiniteList<?>> getAlternative
}

public static <A, B> FiniteList<B> map(FiniteList<A> finiteList, Function<A, B> mapping) {
return finiteList.head().maybe(h -> FiniteList.cons(mapping.apply(h), map(finiteList.tail(), mapping)), FiniteList.nil());
return finiteList.map(mapping);
}

public static <A> FiniteList<A> disjunction(FiniteList<A> op1, FiniteList<A> op2) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.dan323.functional.data.list;

import com.dan323.functional.data.optional.Maybe;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.UnaryOperator;

Expand All @@ -20,8 +17,8 @@ final class Generating<A> extends InfiniteList<A> {
}

@Override
public Maybe<A> head() {
return Maybe.of(head);
public A getHead() {
return head;
}

@Override
Expand All @@ -36,17 +33,17 @@ public <B> InfiniteList<B> map(Function<A, B> mapping) {

protected static final class GeneratingMapped<A,B> extends InfiniteList<B> {

private final List<A> originalList;
private final InfiniteList<A> originalList;
private final Function<A,B> mapping;

GeneratingMapped(List<A> originalList, Function<A,B> mapping){
GeneratingMapped(InfiniteList<A> originalList, Function<A,B> mapping){
this.mapping = mapping;
this.originalList = originalList;
}

@Override
public Maybe<B> head() {
return originalList.head().maybe(h -> Maybe.of(mapping.apply(h)), Maybe.of());
public B getHead() {
return mapping.apply(originalList.getHead());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
package com.dan323.functional.data.list;

import com.dan323.functional.data.optional.Maybe;

import java.util.function.Function;

/**
* Interface for infinite lists, lists implementing this interface should compute the elements lazily (only when queried)
*
* @param <A> type of elements in the list
*/
public sealed abstract class InfiniteList<A> implements List<A> permits Cons, Generating, Generating.GeneratingMapped, Repeat, Zipped {
public abstract sealed class InfiniteList<A> implements List<A> permits Cons, Cycle, Generating, Generating.GeneratingMapped, Repeat, Zipped {

@Override
public abstract InfiniteList<A> tail();

@Override
public abstract <B> InfiniteList<B> map(Function<A, B> mapping);

public abstract A getHead();

@Override
abstract public InfiniteList<A> tail();
public Maybe<A> head() {
return Maybe.of(getHead());
}

@Override
abstract public <B> InfiniteList<B> map(Function<A, B> mapping);
public InfiniteList<A> cons(A head) {
return new Cons<>(head, this);
}

/**
* Infinite lists are incomparable in a finite amount of time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,14 @@ public sealed interface List<A> permits FiniteList, InfiniteList {

List<A> tail();

List<A> cons(A head);

<B> List<B> map(Function<A,B> mapping);

static <A> List<A> generate(A first, UnaryOperator<A> generator){
static <A> InfiniteList<A> generate(A first, UnaryOperator<A> generator){
Comment thread
dan323 marked this conversation as resolved.
return new Generating<>(first, generator);
}

static <A> List<A> cons(A first, List<A> tail) {
if (first == null || tail == null) {
throw new IllegalArgumentException("No input can be null");
}
if (tail instanceof FiniteList<A> finiteTail){
return new FinCons<>(first, finiteTail);
} else {
return new Cons<>(first, (InfiniteList<A>) tail);
}
}

default FiniteList<A> limit(int k){
return head().maybe(h -> limitWithHead(h, k), FiniteList.nil());
}
Expand All @@ -40,11 +31,21 @@ private FiniteList<A> limitWithHead(A h, int k){
}
}

static <A> List<A> nil() {
static <A> List<A> cycle(FiniteList<A> lst){
if (lst.length() == 0) {
return nil();
} else if (lst.length() == 1) {
return lst.head().maybe(List::repeat,nil());
} else {
return new Cycle<>(lst);
}
}

static <A> FiniteList<A> nil() {
return (FiniteList<A>) Nil.NIL;
}

static <A> List<A> repeat(A a){
static <A> InfiniteList<A> repeat(A a){
return new Repeat<>(a);
}
Comment thread
dan323 marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ public static <A> FiniteList<A> reverse(FiniteList<A> lst) {
}

public static <A> FiniteList<A> concat(FiniteList<A> a, FiniteList<A> b){
return a.head().maybe(h -> FiniteList.cons(h, concat(a.tail(), b)), b);
return a.head().maybe(h -> concat(a.tail(), b).cons(h), b);
}

public static <A> InfiniteList<A> concat(FiniteList<A> a, InfiniteList<A> b){
return a.head().maybe(h -> concat(a.tail(), b).cons(h), b);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package com.dan323.functional.data.list;

import com.dan323.functional.data.optional.Maybe;

import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
Expand All @@ -21,8 +17,8 @@ final class Repeat<A> extends InfiniteList<A> {
}

@Override
public Maybe<A> head() {
return Maybe.of(element);
public A getHead() {
return element;
}

@Override
Expand Down
Loading