Skip to content

Commit 130d382

Browse files
author
Daniel
committed
FP: move/create functional examples here
1 parent c5adb8d commit 130d382

7 files changed

Lines changed: 1127 additions & 20 deletions

File tree

TOPICS_PLAN.md

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ Reorganized by interview topics instead of Java versions.
88
src/main/java/org/nkcoder/
99
├── concurrency/ # Multithreading & Concurrency
1010
├── collections/ # Collections Framework
11-
├── streams/ # Streams & Functional Programming
11+
├── streams/ # Stream API
12+
├── fp/ # Functional Programming
1213
├── oop/ # OOP & Modern Classes (Records, Sealed)
1314
├── pattern/ # Pattern Matching
1415
├── strings/ # String API
@@ -51,21 +52,31 @@ src/main/java/org/nkcoder/
5152

5253
---
5354

54-
### 3. streams/ - Streams & Functional Programming
55+
### 3. streams/ - Stream API
56+
57+
| Status | Example | Concepts |
58+
|--------|-------------------------|----------------------------------------------------|
59+
| [x] | StreamBasicsExample | Creating streams, intermediate/terminal operations |
60+
| [x] | StreamCollectorsExample | Collectors, groupingBy, partitioningBy, toMap |
61+
| [x] | ParallelStreamExample | Parallel streams, when to use, pitfalls |
62+
| [x] | StreamAdvancedExample | flatMap, reduce, takeWhile, dropWhile |
63+
64+
---
65+
66+
### 4. fp/ - Functional Programming
5567

5668
| Status | Example | Concepts |
5769
|--------|----------------------------|-----------------------------------------------------|
58-
| [x] | StreamBasicsExample | Creating streams, intermediate/terminal operations |
59-
| [x] | StreamCollectorsExample | Collectors, groupingBy, partitioningBy, toMap |
60-
| [x] | ParallelStreamExample | Parallel streams, when to use, pitfalls |
61-
| [x] | OptionalExample | Optional creation, chaining, orElse vs orElseGet |
62-
| [x] | FunctionalInterfaceExample | Function, Predicate, Consumer, Supplier, custom |
6370
| [x] | LambdaExample | Lambda syntax, method references, effectively final |
64-
| [x] | StreamAdvancedExample | flatMap, reduce, takeWhile, dropWhile |
71+
| [x] | FunctionalInterfaceExample | Function, Predicate, Consumer, Supplier, custom |
72+
| [x] | OptionalExample | Optional creation, chaining, orElse vs orElseGet |
73+
| [x] | CurryingExample | Currying, partial application, function factories |
74+
| [x] | MemoizationExample | Caching pure functions, lazy computation, Fibonacci |
75+
| [x] | MonadPatternsExample | Try monad, Validation, railway-oriented programming |
6576

6677
---
6778

68-
### 4. oop/ - OOP & Modern Classes
79+
### 5. oop/ - OOP & Modern Classes
6980

7081
| Status | Example | Concepts |
7182
|--------|---------------------------|-----------------------------------------------------|
@@ -78,7 +89,7 @@ src/main/java/org/nkcoder/
7889

7990
---
8091

81-
### 5. pattern/ - Pattern Matching
92+
### 6. pattern/ - Pattern Matching
8293

8394
| Status | Example | Concepts |
8495
|--------|--------------------------|----------------------------------------------|
@@ -91,7 +102,7 @@ src/main/java/org/nkcoder/
91102

92103
---
93104

94-
### 6. strings/ - String API
105+
### 7. strings/ - String API
95106

96107
| Status | Example | Concepts |
97108
|--------|---------------------------|-----------------------------------------------------|
@@ -103,7 +114,7 @@ src/main/java/org/nkcoder/
103114

104115
---
105116

106-
### 7. generics/ - Generics
117+
### 8. generics/ - Generics
107118

108119
| Status | Example | Concepts |
109120
|--------|-----------------------|---------------------------------------------|
@@ -115,7 +126,7 @@ src/main/java/org/nkcoder/
115126

116127
---
117128

118-
### 8. exceptions/ - Exception Handling
129+
### 9. exceptions/ - Exception Handling
119130

120131
| Status | Example | Concepts |
121132
|--------|---------------------------|----------------------------------------------------------------|
@@ -126,7 +137,7 @@ src/main/java/org/nkcoder/
126137

127138
---
128139

129-
### 9. io/ - I/O & Networking
140+
### 10. io/ - I/O & Networking
130141

131142
| Status | Example | Concepts |
132143
|--------|---------------------------|----------------------------------------|
@@ -142,11 +153,12 @@ src/main/java/org/nkcoder/
142153
|--------------|-------------|----------|
143154
| concurrency | In Progress | 8/10 |
144155
| collections | Complete | 6/6 |
145-
| streams | Not Started | 0/7 |
146-
| oop | Not Started | 0/6 |
156+
| streams | Complete | 4/4 |
157+
| fp | Complete | 6/6 |
158+
| oop | Complete | 6/6 |
147159
| pattern | Not Started | 0/6 |
148160
| strings | Not Started | 0/5 |
149161
| generics | Not Started | 0/5 |
150162
| exceptions | Not Started | 0/4 |
151163
| io | Not Started | 0/3 |
152-
| **Total** | | **14/52**|
164+
| **Total** | | **30/55**|
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package org.nkcoder.fp;
2+
3+
import java.util.function.BiFunction;
4+
import java.util.function.Function;
5+
import java.util.function.IntFunction;
6+
import java.util.function.UnaryOperator;
7+
8+
/**
9+
* Currying and Partial Application in Java.
10+
*
11+
* <ul>
12+
* <li>Currying: Transform multi-arg function into chain of single-arg functions</li>
13+
* <li>Partial Application: Fix some arguments, return new function</li>
14+
* <li>Function Factories: Create specialized functions from general ones</li>
15+
* <li>Enables better code reuse and composition</li>
16+
* </ul>
17+
*/
18+
public class CurryingExample {
19+
20+
static void main(String[] args) {
21+
whatIsCurrying();
22+
partialApplication();
23+
functionFactories();
24+
realWorldExamples();
25+
}
26+
27+
// ============ What is Currying ============
28+
29+
static void whatIsCurrying() {
30+
System.out.println("=== What is Currying ===");
31+
32+
// Traditional: function with 2 arguments
33+
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
34+
System.out.println("add(3, 5) = " + add.apply(3, 5));
35+
36+
// Curried: chain of single-argument functions
37+
// f(a, b) becomes f(a)(b)
38+
Function<Integer, Function<Integer, Integer>> curriedAdd = a -> b -> a + b;
39+
System.out.println("curriedAdd(3)(5) = " + curriedAdd.apply(3).apply(5));
40+
41+
// Step by step
42+
Function<Integer, Integer> add3 = curriedAdd.apply(3); // Partially apply first arg
43+
System.out.println("add3(5) = " + add3.apply(5));
44+
System.out.println("add3(10) = " + add3.apply(10));
45+
46+
// Curried function with 3 arguments
47+
Function<Integer, Function<Integer, Function<Integer, Integer>>> curriedSum3 =
48+
a -> b -> c -> a + b + c;
49+
System.out.println("curriedSum3(1)(2)(3) = " + curriedSum3.apply(1).apply(2).apply(3));
50+
51+
// Generic curry helper
52+
System.out.println("\nUsing curry helper:");
53+
BiFunction<String, String, String> concat = (a, b) -> a + b;
54+
var curriedConcat = curry(concat);
55+
System.out.println("curriedConcat(\"Hello \")(\"World\") = " +
56+
curriedConcat.apply("Hello ").apply("World"));
57+
58+
System.out.println("""
59+
60+
Currying transforms f(a, b) into f(a)(b)
61+
- Each function takes ONE argument
62+
- Returns a function for the next argument
63+
- Named after Haskell Curry (mathematician)
64+
""");
65+
}
66+
67+
// Helper: Convert BiFunction to curried form
68+
static <A, B, R> Function<A, Function<B, R>> curry(BiFunction<A, B, R> f) {
69+
return a -> b -> f.apply(a, b);
70+
}
71+
72+
// Helper: Convert curried function back to BiFunction
73+
static <A, B, R> BiFunction<A, B, R> uncurry(Function<A, Function<B, R>> f) {
74+
return (a, b) -> f.apply(a).apply(b);
75+
}
76+
77+
// ============ Partial Application ============
78+
79+
static void partialApplication() {
80+
System.out.println("=== Partial Application ===");
81+
82+
// Original function
83+
TriFunction<String, String, String, String> formatMessage =
84+
(level, timestamp, message) -> "[" + level + "] " + timestamp + ": " + message;
85+
86+
String fullMessage = formatMessage.apply("INFO", "2024-01-15", "Server started");
87+
System.out.println("Full: " + fullMessage);
88+
89+
// Partial application: fix the level
90+
BiFunction<String, String, String> infoLogger = partial1(formatMessage, "INFO");
91+
BiFunction<String, String, String> errorLogger = partial1(formatMessage, "ERROR");
92+
93+
System.out.println("Info: " + infoLogger.apply("2024-01-15", "User logged in"));
94+
System.out.println("Error: " + errorLogger.apply("2024-01-15", "Connection failed"));
95+
96+
// Partial application: fix level and timestamp
97+
Function<String, String> todayInfoLogger = partial2(formatMessage, "INFO", "2024-01-15");
98+
System.out.println("Today Info: " + todayInfoLogger.apply("Processing complete"));
99+
100+
// Using currying for partial application
101+
Function<String, Function<String, Function<String, String>>> curriedFormat =
102+
level -> timestamp -> message -> "[" + level + "] " + timestamp + ": " + message;
103+
104+
var debugLogger = curriedFormat.apply("DEBUG");
105+
var debugTodayLogger = debugLogger.apply("2024-01-15");
106+
System.out.println("Debug Today: " + debugTodayLogger.apply("Cache hit"));
107+
108+
System.out.println("""
109+
110+
Partial Application fixes some arguments:
111+
- f(a, b, c) with a fixed becomes g(b, c)
112+
- Creates specialized versions of general functions
113+
- Different from currying (which chains single-arg functions)
114+
""");
115+
}
116+
117+
// Custom TriFunction interface
118+
@FunctionalInterface
119+
interface TriFunction<A, B, C, R> {
120+
R apply(A a, B b, C c);
121+
}
122+
123+
// Partial application helpers
124+
static <A, B, C, R> BiFunction<B, C, R> partial1(TriFunction<A, B, C, R> f, A a) {
125+
return (b, c) -> f.apply(a, b, c);
126+
}
127+
128+
static <A, B, C, R> Function<C, R> partial2(TriFunction<A, B, C, R> f, A a, B b) {
129+
return c -> f.apply(a, b, c);
130+
}
131+
132+
// ============ Function Factories ============
133+
134+
static void functionFactories() {
135+
System.out.println("=== Function Factories ===");
136+
137+
// Multiplier factory
138+
IntFunction<UnaryOperator<Integer>> multiplier = factor -> x -> x * factor;
139+
var double_ = multiplier.apply(2);
140+
var triple = multiplier.apply(3);
141+
142+
System.out.println("double(5) = " + double_.apply(5));
143+
System.out.println("triple(5) = " + triple.apply(5));
144+
145+
// Adder factory
146+
Function<Integer, UnaryOperator<Integer>> adder = n -> x -> x + n;
147+
var add10 = adder.apply(10);
148+
var add100 = adder.apply(100);
149+
150+
System.out.println("add10(5) = " + add10.apply(5));
151+
System.out.println("add100(5) = " + add100.apply(5));
152+
153+
// Comparator factory
154+
Function<Integer, Function<Integer, Boolean>> greaterThan = threshold -> value -> value > threshold;
155+
var greaterThan10 = greaterThan.apply(10);
156+
var greaterThan100 = greaterThan.apply(100);
157+
158+
System.out.println("greaterThan10(15) = " + greaterThan10.apply(15));
159+
System.out.println("greaterThan100(15) = " + greaterThan100.apply(15));
160+
161+
// Prefix/suffix factory
162+
Function<String, UnaryOperator<String>> prefixer = prefix -> s -> prefix + s;
163+
Function<String, UnaryOperator<String>> suffixer = suffix -> s -> s + suffix;
164+
165+
var addHello = prefixer.apply("Hello, ");
166+
var addBang = suffixer.apply("!");
167+
var greet = addHello.andThen(addBang);
168+
169+
System.out.println("greet(\"World\") = " + greet.apply("World"));
170+
171+
// Compose factories
172+
var pipeline = double_.andThen(add10).andThen(triple);
173+
System.out.println("pipeline(5) = (5*2 + 10) * 3 = " + pipeline.apply(5));
174+
175+
System.out.println("""
176+
177+
Function Factories create specialized functions:
178+
- Take configuration, return function
179+
- Enable code reuse and DRY principle
180+
- Compose well with andThen/compose
181+
""");
182+
}
183+
184+
// ============ Real World Examples ============
185+
186+
static void realWorldExamples() {
187+
System.out.println("=== Real World Examples ===");
188+
189+
// 1. URL Builder
190+
System.out.println("-- URL Builder --");
191+
Function<String, Function<String, Function<String, String>>> urlBuilder =
192+
protocol -> host -> path -> protocol + "://" + host + path;
193+
194+
var httpsBuilder = urlBuilder.apply("https");
195+
var apiBuilder = httpsBuilder.apply("api.example.com");
196+
197+
System.out.println(apiBuilder.apply("/users"));
198+
System.out.println(apiBuilder.apply("/products"));
199+
200+
// 2. Validator Factory
201+
System.out.println("\n-- Validator Factory --");
202+
Function<Integer, Function<Integer, Function<String, String>>> rangeValidator =
203+
min -> max -> value -> {
204+
try {
205+
int num = Integer.parseInt(value);
206+
return (num >= min && num <= max) ? "Valid" : "Out of range [" + min + "-" + max + "]";
207+
} catch (NumberFormatException e) {
208+
return "Not a number";
209+
}
210+
};
211+
212+
var ageValidator = rangeValidator.apply(0).apply(150);
213+
var percentValidator = rangeValidator.apply(0).apply(100);
214+
215+
System.out.println("Age '25': " + ageValidator.apply("25"));
216+
System.out.println("Age '200': " + ageValidator.apply("200"));
217+
System.out.println("Percent '50': " + percentValidator.apply("50"));
218+
219+
// 3. String Formatter Factory
220+
System.out.println("\n-- String Formatter Factory --");
221+
Function<String, Function<String, UnaryOperator<String>>> wrapper =
222+
prefix -> suffix -> s -> prefix + s + suffix;
223+
224+
var htmlBold = wrapper.apply("<b>").apply("</b>");
225+
var htmlItalic = wrapper.apply("<i>").apply("</i>");
226+
var parentheses = wrapper.apply("(").apply(")");
227+
228+
System.out.println(htmlBold.apply("important"));
229+
System.out.println(htmlItalic.apply("emphasized"));
230+
System.out.println(parentheses.apply("note"));
231+
232+
// 4. Discount Calculator
233+
System.out.println("\n-- Discount Calculator --");
234+
Function<Double, UnaryOperator<Double>> discountBy =
235+
percent -> price -> price * (1 - percent / 100);
236+
237+
var tenPercentOff = discountBy.apply(10.0);
238+
var twentyPercentOff = discountBy.apply(20.0);
239+
var halfPrice = discountBy.apply(50.0);
240+
241+
double originalPrice = 100.0;
242+
System.out.printf("Original: $%.2f%n", originalPrice);
243+
System.out.printf("10%% off: $%.2f%n", tenPercentOff.apply(originalPrice));
244+
System.out.printf("20%% off: $%.2f%n", twentyPercentOff.apply(originalPrice));
245+
System.out.printf("50%% off: $%.2f%n", halfPrice.apply(originalPrice));
246+
247+
System.out.println("""
248+
249+
Real-world uses of currying/partial application:
250+
- Configuration builders (URLs, queries)
251+
- Validation with configurable rules
252+
- Text formatting and templating
253+
- Price calculators with configurable discounts
254+
- Logger factories with preset levels
255+
""");
256+
}
257+
}

src/main/java/org/nkcoder/streams/FunctionalInterfaceExample.java renamed to src/main/java/org/nkcoder/fp/FunctionalInterfaceExample.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nkcoder.streams;
1+
package org.nkcoder.fp;
22

33
import java.util.List;
44
import java.util.Objects;

src/main/java/org/nkcoder/streams/LambdaExample.java renamed to src/main/java/org/nkcoder/fp/LambdaExample.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.nkcoder.streams;
1+
package org.nkcoder.fp;
22

33
import java.util.Comparator;
44
import java.util.List;

0 commit comments

Comments
 (0)