Functional Programming in Java
Table of contents
- Introduction
- Imperative Programming
- Declarative Programming
- Functional Programming
- Tangible Improvements
- The Big Gains of Functional-Style Code
- Why Code in Functional Style?
- Hassle-Free Concurrency
- Side Effect
- Anonymous Inner Class
- Lambda Expression
- Lambda Expressions vs Anonymous Classes
- Lazy Evaluation
- Laziness of Streams
- Pure OOP vs Hybrid OOP-Functional Style
- Essential Practices to Succeed with Functional Style
- Passing Method References
- Dominant Functional Programming Languages
- Functional Programming Languages Recommendation by Learning Curves and Pragmatic and Future vision
- Job Trends for Functional Programming Languages
- Further Reading
- Book
Introduction
Since Java 8, Functional Programming is supported. Functional Programming techniques are designed and implemented using lambdas. Most of the Functional Programming libraries are available in java.util.function
.
Imperative Programming
An Imperative Programming paradigm describes operations in terms of the program's state and the statements that change that state. It is the opposite concept of Declarative Programming.
The earliest imperative languages were machine languages (Assembly).
Over the past 20 years, many high-level languages like C, C++, and Java have been developed, but these languages are also imperative.
Declarative Programming
Declarative Programming focuses on what the program should accomplish rather than how to accomplish it.
Example: Web pages describe what should appear—titles, fonts, images—not how to render them on the screen.
Imperative programming languages specify algorithms but do not specify goals.
Declarative programming languages specify goals but do not specify algorithms.
Functional Programming
A paradigm that emerged to support a pure functional approach to problem-solving. It is a form of Declarative Programming.
Functions are treated as first-class citizens, so they can be passed as arguments to other functions or returned as function values.
The result of a function depends only on its input values.
Avoids side effects.
Functional vs Imperative
Feature | Imperative | Functional |
Programmer's View | How to perform tasks and how to track the state | What information is needed and what transformations are required |
State Changes | Important | Non-existent |
Order of Execution | Important | Less important |
Main Flow Control | Loops, conditionals, function calls | Function calls, including recursion |
Main Units | Instances of structures or classes | Functions as first-class citizens and data collections |
The Habitual Way
boolean found = false;
for(String city : cities) {
if(city.equals("Chicago")) {
found = true;
break;
}
}
System.out.println("Found chicago?:" + found);
A Better Way
System.out.println("Found chicago?:" + cities.contains(“Chicago"));
Tangible Improvements
No mutable variables.
No loop statements.
Less code.
Clearer: Allows us to focus on our main concern.
Reduces errors.
Easier to understand and maintain.
The Old Way
public class Prices {
public static final List<BigDecimal> prices = Arrays.asList(
new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"),
new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"),
new BigDecimal("45"), new BigDecimal("12"));
public static void main(String[] args) {
BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO;
for(BigDecimal price : prices) {
if(price.compareTo(BigDecimal.valueOf(20)) > 0)
totalOfDiscountedPrices =
totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9)));
}
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
// Instead of directly computing with primitive types,
// consider creating a separate class and providing operations through APIs. }
}
The Old Way: problems
Primitive Obsession
Violates the Single Responsibility Principle (SRP)
Primitive Obsession: When the code relies too much on primitives. And primitive value controls the logic in a class and this primitive value is not type safe.
Single Responsibility Principle: There should never be more than one reason for a class to chang
A Better Way again
final BigDecimal totalOfDiscountedPrices =
prices.stream()
.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
map
filter
reduce
Reduce (foldLeft)
The Improvements
The code is well-structured and not cluttered.
No need to worry about low-level operations.
Easier to improve or change the logic.
Control loops using library methods.
Efficient: Loops apply lazy evaluation.
Easy to modify for parallel execution where desired.
The Big Gains of Functional-Style Code
By not using mutable variables or reassigning variables, there are fewer bugs, and it's easier to write code considering concurrency.
Fewer mutable variables mean fewer errors in the code.
Easier to write thread-safe code, reducing concerns about concurrency issues.
Allows us to express our thoughts clearly in code.
Concise code reduces writing and reading time, making maintenance easier.
Why Code in Functional Style?
Iteration on Steroids
Enforcing Policies
Extending Policies
Hassle-Free Concurrency
Iteration on Steroids
We often use sets and maps to handle lists of objects, using loops. Even for simple iterations, we had to use for
loops. Since Java 8, various methods are provided to handle these lists in different ways.
public static int sumByImperativeWay() {
int sum = 0; // mutable variable
for (int i : list) { // iteration
sum += i;
}
return sum;
}
public static int sumByFunctionalWay() {
// no iteration, no mutable variable, concise
return list.stream().mapToInt(Integer::intValue).sum();
}
Enforcing Policies
Enterprise applications are maintained by policies.
Examples:
Verifying that a specific operation has appropriate security credentials.
Ensuring that a transaction is fast and updates are performed correctly.
Transaction transaction = getFromTransactionFactory();
// operation to run within the transaction ..
checkProgressAndCommitOrRollbackTransaction();
UpdateAuditTrail();
Causes code duplication and increases maintenance costs.
Exceptions can cause transactions to fail, requiring careful code management.
runWithinTransaction((Transaction transaction) -> {
// .. operation to run within the transaction ..
});
Instead of obtaining a transaction directly, use a well-managed function to perform only the desired operations.
Policy code for status and updates is abstracted and encapsulated.
No need to think about exceptions or transaction-related issues.
Extending Policies
기업의 정책은 변경이 되고 확장이 된다. (여러 개의 연산을 삭제/추가)
그래서 Core Logic 을 분석하고 이를 잘 사용할 수 있도록 코드를 작성해야 한다.
이러한 복잡한 일련의 절차는 종종 한 개 이상의 Interface 들을 사용하여 구현한다.
이러한 방법은 효율적이나 많은 수의 Interface 를 생산하게 되어 유지 보수가 어려워지는 단점이 존재한다.
Extending Policies: In Functional style
Corporate policies change and expand (adding/deleting multiple operations). Therefore, we need to analyze the core logic and write code that can use it effectively. Such complex procedures are often implemented using more than one interface. While efficient, this method produces many interfaces, making maintenance difficult.
Functional Style:
Use Functional Interfaces and Lambda Expressions to replace traditional methods of extending policies.
No need to create additional interfaces, so no need to implement methods.
We can focus only on implementing the core logic.
public class Camera {
..
public void setFilters(final Function<Color, Color>... filters) {
filter =
Stream.of(filters)
.reduce((filter, next) -> filter.compose(next))
.orElseGet(Function::identity);
}
..
}
camera.setFilters(Color::brighter, Color::darker);
Implement the Decorator Pattern by chaining lambda expressions.
Similarly, policies can be extended by chaining lambda expressions easily.
No need to design interfaces to understand the class structure.
Hassle-Free Concurrency
When a large application is about to be deployed, performance issues may arise, revealing that the biggest bottleneck is in a module that processes large amounts of data. It's suggested that processing this module using multiple cores will improve performance immediately (it was being processed sequentially). In such cases, we need to modify the code to allow the module to operate in parallel. However, with code written in an imperative style, there's much to consider. But with code written in a functional style, there's little to change.
// sequential: 단일 thread 로 처리
sequential: findHighPriced(items.stream());
// parallel: thread pool 로 관리되는 multi threads 로 처리, Simple!!
parallel: findHighPriced(items.parallelStream());
Parallelization
Decide whether the lambda expressions should be executed concurrently.
Determine if they can be executed without race conditions or side effects.
Check if the results of concurrently executed lambda expressions depend on the order of processing.
For collections with small time/number, sequential processing is advantageous.
For collections with large time/number, parallel processing is advantageous.
Side Effect
An act that changes the state in the execution environment.
- Modifying an object by calling an I/O function or another function, or changing the value of an lvalue.
x = 1 + 2; // side effect lvalue: x
y = i++; // side effect, i, y
1 * 2; // no side effect
func(Object object) { // side effect on object
object.setValue(1);
}
Problems with Side Effects
public class RandomGenerator {
public int seed { get; set; }
public int getRandomValue() {
Random(seed);
seed++; // The state change of 'seed' is not apparent, making debugging difficult.
}
}
Advantages Without Side Effects
The javac compiler can better optimize functions without side effects.
Functions without side effects can have their execution order rearranged, making optimization easier.
If F1 and F2 are functions performing independent tasks, it's easier to optimize or adjust the execution order.
Anonymous Inner Class
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println(“Action added”);
}
});
Without declaring a class that implements the ActionListener interface, we can implement the required methods directly.
Commonly used to add event handler logic.
Inconvenient because functions cannot be passed as arguments, requiring us to write such code.
Lambda Expression
button.addActionListener(
event -> System.out.println(“Action added”)
);
Functions without names, consisting of arguments and a function body.
Syntax:
(arg1, arg2, ...) -> { body } (type arg1, type arg2, ...) -> { body }
Lambda Expressions vs Anonymous Classes
Aspect | Lambda Expression | Anonymous Class |
this Reference | Class where the lambda expression is used | Anonymous class |
Compiled | Converted to a private method | Method with the same signature |
Instruction | invokedynamic | Similar instruction conversion principle |
Lambda Expression
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World”);
(String s) -> { System.out.println(s); }
() -> 42 // Returns 42 without any arguments
() -> { return 3.1415 };
Functional Interface
An interface declared with only one abstract method.
Examples:
java.lang.Runnable
java.awt.event.ActionListener
We can create instances of functional interfaces using anonymous inner classes. Alternatively, we can use lambda expressions to simplify.
// The @FunctionalInterface annotation allows the compiler to check
// whether the interface is a functional interface.
@FunctionalInterface
public interface Worker {
public void doSomeWork();
}
public class WorkerTester {
public static void execute(Worker worker) {
worker.doSomeWork();
}
public static void main(String[] args) {
execute(new Worker() {
@Override
public void doSomeWork() {
System.out.printf("do some heavy work");
}
});
execute(() -> System.out.printf("do some work by lambda"));
}
}
Lambda for Collection
// Data
final List<String> friends = Arrays.asList("Brian", "Nate", "Neal", "Raju","Sara", “Scott");
Collection Iteration
// The habitual way
for(String name : friends) {
System.out.print(name);
}
// In lambda way, type inference, name: String name
friends.forEach(name -> System.out.println(name));
Collection Transform
// The habitual way
List<String> upperCaseList = new ArrayList<String>();
for(String name : friends) {
upperCaseList.add(name.toUpperCase());
}
// In lambda way
friends.stream().map(name -> name.toUpperCase());
Function Composition
To transform objects over multiple stages, functions can be combined as follows. In functional languages, we use Function Composition to write code for combining operations.
symbols.map(Goods::getPrice)
.filter(Goods.isPriceLessThan(500))
.reduce(Goods::pickHigh)
.get();
Collection - Find Elements
// The habitual way
List<String> nameStartsWithN = new ArrayList<String>();
for(String name : friends) {
if (name.startsWith(“N”)
nameStartsWith.add(name);
}
// In lambda way
friends.stream().filter(name -> name.startsWith(“N”));
Creating Fluent Interfaces using Lambda Expressions
public class Mailer {
public void from(final String address) { /*... */ }
public void to(final String address) { /*... */ }
public void subject(final String line) { /*... */ }
public void body(final String message) { /*... */ }
public void send() { System.out.println("sending..."); }
…
public static void main(final String[] args) {
Mailer mailer = new Mailer(); // Uncertain object lifespan
mailer.from("build@agiledeveloper.com"); // Repeated object
mailer.to("starblood@agiledeveloper.com"); // Repeated object
mailer.subject("Build Notification"); // Repeated object
mailer.body("...your code needs improvement...");
mailer.send();
}
}
Two smells
Repeated reference variable usage
Unclear lifecycle of the
mailer
object—how should it be handled? Is it reusable?
Points for Improvement
Repeated reference variable usage.
It's better to maintain a conversational state within the context of the object.
We can improve by using Method Chaining or the Cascade Method Pattern.
Implementing Method Chaining
Change the return type of methods from
void
to the instance of the object.The returned object can be used to build a method chain.
public class MailBuilder {
public MailBuilder from(final String address) { /*... */; return this; }
public MailBuilder to(final String address) { /*... */; return this; }
public MailBuilder subject(final String line) { /*... */; return this; }
public MailBuilder body(final String message) { /*... */; return this; }
public void send() { System.out.println("Sending..."); }
public static void main(final String[] args) {
new MailBuilder() // Someone might store the object
.from("build@agiledeveloper.com")
.to("starblood@agiledeveloper.com")
.subject("Build Notification")
.body("...improving...")
.send();
}
}
- Let's make it more flexible using lambdas!
Improved Version
public class FluentMailer {
private FluentMailer() {} // Prevent direct object creation
public FluentMailer from(final String address) { /*... */; return this; }
public FluentMailer to(final String address) { /*... */; return this; }
public FluentMailer subject(final String line) { /*... */; return this; }
public FluentMailer body(final String message) { /*... */; return this; }
// Accepts a code block with a FluentMailer instance as an argument
public static void send(final Consumer<FluentMailer> block) {
final FluentMailer mailer = new FluentMailer();
block.accept(mailer);
System.out.println("Sending...");
}
public static void main(final String[] args) {
// Use the 'mailer' object within a lambda expression - Loan Pattern
FluentMailer.send(mailer -> // No need to call 'new'
mailer.from("build@agiledeveloper.com")
.to("starblood@agiledeveloper.com")
.subject("Build Notification")
.body("...much better..."));
}
}
Lazy Evaluation
The goal is to execute computationally expensive tasks minimally.
func1() || func2()
:func2
is not executed iffunc1
istrue
(short-circuiting).func(func1(), func2())
: Bothfunc1
andfunc2
must be executed to performfunc
.
We can use lambda expressions to achieve lazy evaluation.
public static boolean evaluate(final int value) {
System.out.println("Evaluating..." + value);
simulateTimeConsumingOp(2000);
return value > 100;
}
public static void eagerEvaluator(final boolean input1, final boolean input2) {
System.out.println("eagerEvaluator called...");
System.out.println("Accept?: " + (input1 && input2));
}
public static void lazyEvaluator(final Supplier<Boolean> input1, final Supplier<Boolean> input2) {
System.out.println("lazyEvaluator called...");
System.out.println("Accept?: " + (input1.get() && input2.get()));
}
System.out.println("// START:EAGER_OUTPUT");
eagerEvaluator(evaluate(1), evaluate(2));
System.out.println("// END:EAGER_OUTPUT");
System.out.println("// START:LAZY_OUTPUT");
lazyEvaluator(() -> evaluate(1), () -> evaluate(2));
System.out.println("// END:LAZY_OUTPUT");
START:EAGER_OUTPUT
Evaluating...1
Evaluating...2
eagerEvaluator called...
Accept?: false
END:EAGER_OUTPUT
START:LAZY_OUTPUT
lazyEvaluator called...
Evaluating...1
Accept?: false
END:LAZY_OUTPUT
Laziness of Streams
Streams have Intermediate and Terminal Operation methods.
Intermediate operation methods can form a chain.
The terminal operation is at the end of the chain.
map
andfilter
are intermediate methods.findFirst
andreduce
are terminal methods.
Leveraging laziness of Stream
Helper Methods for Lazy Evaluation:
private static int length(final String name) {
System.out.println("Getting length for " + name);
return name.length();
}
private static String toUpper(final String name) {
System.out.println("Converting to uppercase: " + name);
return name.toUpperCase();
}
Example:
List<String> names = Arrays.asList("Brad", "Kate", "Kim", "Jack", "Joe",
"Mike", "Susan", "George", "Robert", "Julia", "Parker", "Benson");
System.out.println("// START:CHAIN_OUTPUT");
final String firstNameWith3Letters =
names.stream()
.filter(name -> length(name) == 3)
.map(name -> toUpper(name))
.findFirst()
.get();
System.out.println(firstNameWith3Letters);
Hypothetical Eager Evaluation of Operations:
filter
andmap
methods are lazy.They pass lambda expressions to the next call chain without evaluation.
The expressions are evaluated only when the terminal operation
findFirst
is called.
Creating an infinite Stream of prime numbers
public static boolean isPrime(final int number) {
return number > 1 &&
IntStream.rangeClosed(2, (int) Math.sqrt(number))
.noneMatch(divisor -> number % divisor == 0);
}
private static int primeAfter(final int number) {
if (isPrime(number + 1))
return number + 1;
else
return primeAfter(number + 1);
}
public static List<Integer> primes(final int fromNumber, final int count) {
return Stream.iterate(primeAfter(fromNumber - 1), Primes::primeAfter)
.limit(count)
.collect(Collectors.<Integer>toList());
}
System.out.println("10 primes from 1: " + primes(1, 10));
// Output: 10 primes from 1: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Stream.iterate(seed, UnaryOperator)
: Generates an infinite sequence.UnaryOperator
: A function that takes one argument and returns a result.limit(count)
: Returns a stream consisting of the elements of this stream, truncated to be no longer thancount
in length.
Pure OOP vs Hybrid OOP-Functional Style
OOP: Objects have changing states.
Hybrid: Lightweight objects are transformed into other objects.
Performance Concerns
Do these new features affect performance? Yes.
However, in most cases, these features improve performance.
Efficiency issues that take about 3% of the time can often be ignored.
Since Java 8, due to compiler optimizations, revamped
invokedynamic
, and optimized bytecode instructions, lambda expressions execute quickly.
// Both methods take approximately the same time to compute primesCount.
long primesCount = 0; // Approximately 0.0250944s
for(long number : numbers) {
if(isPrime(number)) primesCount += 1;
}
final long primesCount = // Approximately 0.0253816s
numbers.stream()
.filter(number -> isPrime(number))
.count();
Essential Practices to Succeed with Functional Style
More Declarative, Less Imperative
Favor Immutability
Reduce Side Effects
Prefer Expressions Over Statements
Design with Higher-Order Functions
Lexical Scoping in Closures
public static Predicate<String> checkIfStartsWith(final String letter) {
return name -> name.startsWith(letter);
}
The compiler checks all scopes within the method to find that
letter
is a method parameter (Lexical Scoping).The variable
letter
exists within the scope of thecheckIfStartsWith
method.Lambda expressions can use variables within the method-level scope.
Such lambda expressions are called closures.
Passing Method References
Reference to an Instance Method
Parameterized Lambda Expression Method Call:
friends.stream().map(name -> name.toUpperCase());
Simplified Using Method Reference:
friends.stream().map(String::toUpperCase);
Reference to a Static Method
str.chars().filter(ch -> Character.isDigit(ch));
Simplified Using Method Reference:
str.chars().filter(Character::isDigit);
Reference to a Method on Another Object
str.chars().forEach(ch -> System.out.println(ch));
Simplified Using Method Reference:
str.chars().forEach(System.out::println);
Reference to a Method That Takes Parameters
people.stream().sorted((person1, person2) -> person1.ageDifference(person2));
Simplified Using Method Reference:
people.stream().sorted(Person::ageDifference);
Using a Constructor Reference
Supplier<Heavy> supplier = () -> new Heavy();
Simplified Using Constructor Reference:
Supplier<Heavy> supplier = Heavy::new;
Dominant Functional Programming Languages
Haskell
Supports pure functional programming with a strong static type system and lazy evaluation, offering mathematical precision and high levels of abstraction.
Learning Curve: Very steep due to pure functional programming and unique type system.
Advantages: Concise and mathematically rigorous syntax allows for high abstraction and safety.
Scala
Integrates object-oriented and functional programming, enabling powerful and flexible software development with high-level abstraction and JVM compatibility.
Learning Curve: Steep due to the combination of functional and object-oriented programming.
Advantages: Flexible and expressive syntax allows for effective design of complex systems.
Clojure
A functional programming language featuring concise and expressive syntax and immutable data structures, running on the JVM and effectively supporting concurrency.
Learning Curve: Steep due to Lisp-like syntax and functional paradigm.
Advantages: Enables powerful data processing and concurrency control through concise syntax.
Elixir
A functional programming language with strengths in concurrency and distributed systems, running on the BEAM virtual machine and offering high performance and fault tolerance.
Learning Curve: Slightly steep due to its concurrency model and ecosystem.
Advantages: Allows easy construction of high-performance distributed systems with concise and readable syntax.
Elm
A pure functional language supporting compile-time error prevention and safe front-end web application development, characterized by side-effect-free state management.
Learning Curve: Slightly steep due to pure functional programming and side-effect-free architecture.
Advantages: Enables development of stable and error-free front-end applications through intuitive and strict syntax.
F#
Combines functional and object-oriented programming to write concise and efficient code, featuring excellent compatibility with the .NET platform.
Learning Curve: Slightly steep if unfamiliar with the functional paradigm.
Advantages: Helps solve complex problems easily through concise and intuitive syntax.
Rust
Emphasizes memory safety and performance, featuring safe concurrency that prevents data races and memory errors in system programming.
Learning Curve: Steep due to complexity of memory safety and ownership models.
Advantages: Enables safe and high-performance system programming with clear and expressive syntax.
Type Safe, Strong Type Check, Parallel Friendly, Alternative for C/C++
OCaml
A multi-paradigm language combining functional, object-oriented, and imperative programming, offering a strong type system and fast execution speed.
Learning Curve: Slightly steep due to supporting multiple paradigms with powerful features.
Advantages: Write high-performance and reliable code with a strong type system and concise syntax.
Erlang
Designed for developing distributed systems and high-availability software, featuring a strong concurrency model and fault tolerance.
Learning Curve: Slightly steep due to the need to understand concurrency and distributed system concepts.
Advantages: Simplifies the development of high-availability systems with a concise and unique syntax different from imperative programming.
Kotlin
A modern and concise syntax with complete interoperability on the JVM, optimized for Android development and multi-platform development.
Learning Curve: Gentle due to syntax similar to Java.
Advantages: Improves code readability and productivity with concise and expressive syntax.
Functional Programming Languages Recommendation by Learning Curves and Pragmatic and Future vision
Considering current industry usage, salary levels, and future viability. Personal judgments are included. Levels increase with difficulty.
Kotlin - Easy
Scala - Difficult (Level 2)
- First Salary
Rust - Difficult (Level 2)
Elixir - Difficult (Level 1)
- Third Salary
Clojure - Difficult (Level 2)
- Fourth Salary
F# - Difficult (Level 1)
- Fifth Salary
Haskell - Difficult (Level 3)
- Second Salary
Job Trends for Functional Programming Languages
Languages like Haskell, Scala, Clojure, F#, Erlang, and Elixir have seen varying demand. Scala and Elixir have shown significant growth, while others like Haskell and Clojure have seen steady increases.
Further Reading
Code Smell: Primitive Obsession
Get Rid of That Code Smell – Primitive Obsession
https://zsmahi.github.io/posts/how-to-get-rid-of-primitive-obsession/
Code Smells
SOLID - Single Responsibility Principle
http://blog.sanaulla.info/2011/11/16/solid-single-responsibility-principle/
Functional Programming in Java
http://blog.jetbrains.com/idea/2014/03/functional-programming-with-java-8/
Functional Programming vs. Imperative Programming
https://learn.microsoft.com/en-us/dotnet/standard/linq/functional-vs-imperative-programming
Lambda Expressions
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.htm
Java 8 Lambda Expressions Tutorial With Examples
http://viralpatel.net/blogs/lambda-expressions-java-tutorial/
Book
Functional Programming in Java - Venkat Subramaniam
Purely Functional Data Structures - Chris Okasaki
The Little Schemer - Daniel P. Friedman
Programming in Scala - Martin Odersky
Learn You a Haskell for Great Good!: A Beginner's Guide - Miran Lipovača
Subscribe to my newsletter
Read articles from Gyuhang Shim directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Gyuhang Shim
Gyuhang Shim
Gyuhang Shim Passionate about building robust data platforms, I specialize in large-scale data processing technologies such as Hadoop, Spark, Trino, and Kafka. With a deep interest in the JVM ecosystem, I also have a strong affinity for programming in Scala and Rust. Constantly exploring the intersections of high-performance computing and big data, I aim to innovate and push the boundaries of what's possible in the data engineering world.