Understanding Functional Programming Principles

1. Understanding Pure Functions

PropertyMeaning
DeterministicSame input → same output
No side effectsNo mutation, I/O, exceptions for control flow
Referentially transparentReplace call with value, semantics unchanged

Example: Pure vs impure

// Pure
int add(int a, int b) { return a + b; }
// Impure (reads clock)
long stamp() { return System.currentTimeMillis(); }

2. Understanding Immutability

TechniqueJava Idiom
Final fieldsprivate final
Recordsrecord Point(int x, int y) {} Java 16+
Defensive copyList.copyOf(input)
Builder + freezeBuilder produces immutable
Persistent collectionsVavr / pcollections

3. Understanding Higher-Order Functions

OperationJava Type
Takes functionFunction<T,R>, Predicate<T>
Returns functionSupplier<Function<T,R>>
ConsumerConsumer<T>
Bi-arityBiFunction<T,U,R>

Example: Function as parameter

<T,R> List<R> map(List<T> xs, Function<T,R> f) {
    return xs.stream().map(f).toList();
}

4. Understanding Function Composition

APIDirection
f.andThen(g)g(f(x))
f.compose(g)f(g(x))
Predicate and/or/negateBoolean combinators

Example: Compose pipeline

Function<String,String> pipeline = ((Function<String,String>) String::trim)
    .andThen(String::toLowerCase)
    .andThen(s -> s.replace(" ", "-"));

5. Understanding Referential Transparency

ImplicationBenefit
Memoization safeCache results freely
ParallelizableNo shared mutable state
ReasoningLocal equational reasoning

6. Understanding First-Class Functions

CapabilityJava
Assign to variableFunction<X,Y> f = x -> ...
Pass as argLambda / method reference
Return from methodFunction<X,Y> build()
Store in collectionMap<String, Function<X,Y>>

7. Implementing Map, Filter, Reduce Patterns

OpStream APIResult
map.map(fn)Transform each
filter.filter(pred)Subset
reduce.reduce(id, op)Aggregate
flatMap.flatMap(fn)Flatten nested
collect.collect(Collectors.toMap(...))Materialize

Example: MFR pipeline

long totalCents = orders.stream()
    .filter(Order::isPaid)
    .mapToLong(Order::totalCents)
    .sum();

8. Understanding Currying and Partial Application

ConceptDefinition
Curryingf(a,b,c) → f(a)(b)(c)
Partial applicationFix some args, return new function

Example: Curry in Java

Function<Integer, Function<Integer, Integer>> add = a -> b -> a + b;
Function<Integer, Integer> inc = add.apply(1);
int two = inc.apply(1);  // 2

9. Understanding Closures

AspectJava Behavior
Captured variableMust be effectively final
Capture scopeEnclosing local + instance fields
Mutability workaroundWrap in AtomicReference / array

10. Implementing Monads

MonadJava EquivalentUse
MaybeOptional<T>Absence
EitherVavr Either<L,R>Error or value
ListStream<T>Nondeterminism
FutureCompletableFuture<T>Async

Example: flatMap chain

Optional<String> city = Optional.ofNullable(userId)
    .flatMap(repo::findUser)
    .map(User::address)
    .map(Address::city);

11. Understanding Lazy Evaluation

MechanismJava
StreamsIntermediate ops are lazy until terminal op
SuppliersSupplier<T> defers computation
Memoized lazyDouble-checked volatile field

12. Avoiding Side Effects

PatternReplacement
Mutating paramReturn new value
Logging in pure fnReturn Writer / events
I/O inlineFunctional core, imperative shell
Exception for controlReturn Result type