3262 words
16 minutes
260720_Imperative_VS_FP_basic002

link#


  • Imperative Programming Deep Analysis
    • ✅ Advantages:

        1. Performance Optimization
        1. Debugging Experience
        1. Memory Management
        1. Developer Familiarity
        1. Fine-grained Control
    • ❌ Disadvantages:

        1. Code Verbosity
        1. State Mutation Issues
        1. Limited Compositionality
        1. Testing Challenges
        1. Concurrency Barriers
        1. Maintenance Complexity

  • Functional Programming Deep Analysis
    • ✅ Advantages:

        1. Declarative Style
        1. Enhanced Composability
        1. Immutability Benefits
        1. Superior Testability
        1. Natural Parallelization
        1. Expressiveness & Conciseness
        1. Better Modularity
        1. Modern Language Features
    • ❌ Disadvantages:

        1. Learning Curve
        1. Performance Considerations
        1. Memory Usage Patterns
        1. Debugging Complexity
        1. Integration Challenges


Comprehensive Analysis: Imperative vs Functional Programming|🔝|#

📊 Detailed Comparison Table(C++23으로 비교)#

AspectImperativeFunctionalKey Differences
Control FlowExplicit for/while loops with manual iterationAlgorithm-based (std::transform, std::for_each)FP abstracts iteration control
State ManagementManual state accumulation in mutable variablesDeclarative transformations with minimal stateFP reduces mutability
Iteration Stylefor (auto& file : files) loop controlfiles | views::transform(func) abstractionFP uses iterator composition
Side EffectsDirect manipulation of state within loopsControlled side effects through pure functionsFP isolates side effects
Code Philosophy”How to do it” - step-by-step instructions”What to do” - declarative specificationsDifferent mental models
Variable MutabilityHeavy use of vector<int> results, int line_countImmutable where possible, const variablesFP emphasizes immutability
Function CompositionSequential statements, manual orchestrationComposable operations using pipes/chainsFP enables compositionality
Modern C++ UsageTraditional C++ style (pre-C++20)C++20 ranges, C++23 featuresFP leverages modern standards
Error HandlingManual checking, error codes built into logicCan be integrated with std::expected, std::optionalFP has better error handling
ParallelizationDifficult to parallelize due to shared stateNaturally parallelizable (no shared state)FP better suited for concurrency

🔍 Detailed Advantages and Disadvantages|🔝|#

Imperative Programming Deep Analysis#

✅ Advantages:#

  1. Performance Optimization

    • Direct control over memory allocation
    • Predictable execution flow
    • Often generates more efficient machine code
    • Fine-grained optimization opportunities
  2. Debugging Experience

    • Straightforward step-by-step debugging
    • Clear state inspection at each line
    • Easy to insert breakpoints and inspect variables
    • Call stack is more intuitive
  3. Memory Management

    • Explicit control over allocation/deallocation
    • Predictable memory usage patterns
    • Easier to optimize memory footprint
    • Clear ownership semantics
  4. Developer Familiarity

    • Matches traditional programming education
    • Similar to C, Java, Python styles
    • Lower learning curve for most developers
    • Industry-proven patterns
  5. Fine-grained Control

    • Complete control over execution flow
    • Precise timing and sequence control
    • Easy to implement complex business logic
    • Better for hardware-level operations

❌ Disadvantages:#

  1. Code Verbosity

    • More boilerplate code required
    • Repetitive patterns (initialization, loops, accumulation)
    • Higher maintenance burden
    • More lines of code to maintain
  2. State Mutation Issues

    • Shared mutable state leads to bugs
    • Harder to reason about program state
    • Increased cognitive load when tracking changes
    • Potential for race conditions in concurrent code
  3. Limited Compositionality

    • Difficult to combine operations
    • Tight coupling between steps
    • Hard to reuse individual components
    • Changes in one step affect others
  4. Testing Challenges

    • Harder to unit test individual operations
    • Tests often need to mock complex state
    • Integration tests become necessary
    • Side effects make testing unpredictable
  5. Concurrency Barriers

    • Shared state makes parallelization difficult
    • Requires complex synchronization mechanisms
    • Deadlocks and race conditions more likely
    • Scaling to multiple cores is challenging
  6. Maintenance Complexity

    • Larger codebases become unwieldy
    • Harder to understand existing code
    • More difficult to refactor safely
    • Higher technical debt accumulation

Functional Programming Deep Analysis|🔝|#

✅ Advantages:#

  1. Declarative Style

    • Focuses on what to do, not how
    • More readable and self-documenting
    • Easier to understand intent
    • Reduces implementation details noise
  2. Enhanced Composability

    • Functions can be easily combined
    • Building blocks for complex operations
    • Reusable components
    • Pipeline-style programming
  3. Immutability Benefits

    • Reduces bugs from unexpected state changes
    • Easier to reason about program behavior
    • Thread-safe by default
    • Predictable function outputs
  4. Superior Testability

    • Pure functions are easy to test
    • No complex setup/teardown required
    • Deterministic results
    • Better test coverage possible
  5. Natural Parallelization

    • No shared state to synchronize
    • Easy to distribute computation
    • Better CPU utilization
    • Scales well with cores
  6. Expressiveness & Conciseness

    • More code in fewer lines
    • Higher information density
    • Clearer business logic
    • Less boilerplate
  7. Better Modularity

    • Clear separation of concerns
    • Each function has single responsibility
    • Easy to swap implementations
    • Better code organization
  8. Modern Language Features

    • Leverages C++20 ranges, C++23 std::expected
    • Type-safe error handling
    • Pattern matching capabilities
    • Compiler optimizations friendly

❌ Disadvantages:#

  1. Learning Curve

    • Requires paradigm shift in thinking
    • New concepts (monads, functors, etc.)
    • Different debugging strategies needed
    • More abstract initially
  2. Performance Considerations

    • Potential overhead from abstractions
    • Temporary objects creation
    • Memory allocation patterns
    • May require optimization techniques
  3. Memory Usage Patterns

    • Can create more temporary objects
    • Higher memory footprint sometimes
    • Garbage collection considerations
    • Cache locality impacts
  4. Debugging Complexity

    • Harder to trace execution flow
    • Stack traces can be deeper
    • Lazy evaluation adds complexity
    • Requires different debugging mindset
  5. Integration Challenges

    • May clash with existing imperative code
    • Requires different architecture approach
    • Team adoption barriers
    • Legacy code compatibility issues

🚀 Modern Alternative Pattern Examples|🔝|#

C++23 Modern Functional Approach#

  • The modern C++23 examples demonstrate several advanced patterns:
  1. Error Handling with std::expected

    • Type-safe error handling
    • Explicit success/failure paths
    • Composable error operations
  2. Lazy Evaluation with Ranges

    • On-demand computation
    • Memory efficient for large datasets
    • Composable operations
  3. Functional Pipelines

    • Composable function chains
    • Data flow programming
    • Declarative transformation specification

Rust Functional Programming|🔝|#

  • Rust provides excellent functional programming support:
  1. Iterator-based transformations

    • Zero-cost abstractions
    • Compiler optimizations
    • Memory-efficient patterns
  2. Pattern matching and error handling

    • Result<T, E> type for errors
    • Option<T> for optional values
    • Pattern matching with match
  3. Ownership and borrowing

    • Enforces memory safety
    • No garbage collector needed
    • Thread-safe by design

📈 Performance and Memory Considerations|🔝|#

Imperative Approach#

  • Memory: Generally lower memory footprint
  • CPU: Often faster for simple operations
  • Cache: Better locality of reference
  • Optimization: Easier for compiler to optimize simple loops

Functional Approach#

  • Memory: May use more temporary objects
  • CPU: Can be equally fast with optimizations
  • Cache: May suffer from poor locality
  • Optimization: Modern compilers optimize FP code well

🎯 When to Use Each Approach|🔝|#

Use Imperative When:#

  • Performance is critical and overhead matters
  • Working with hardware-level operations
  • Team lacks functional programming experience
  • Simple, straightforward operations
  • Memory constraints are tight

Use Functional When:#

  • Code maintainability is priority
  • Complex data transformations needed
  • Concurrent/parallel processing required
  • Testing and correctness are critical
  • Working with modern C++ standards

🔧 Migration Strategies|🔝|#

  1. Gradual Adoption

    • Start with small utility functions
    • Use functional patterns where they fit naturally
    • Don’t force functional style everywhere
  2. Hybrid Approach

    • Combine both paradigms
    • Use functional for data processing
    • Use imperative for business logic
  3. Team Training

    • Invest in functional programming education
    • Code reviews focusing on functional patterns
    • Pair programming to share knowledge

📝 Code Quality Metrics|🔝|#

MetricImperativeFunctional
Lines of CodeHigherLower
Cyclomatic ComplexityHigherLower
Test CoverageHarder to achieveEasier to achieve
Maintainability IndexLowerHigher
Defect DensityHigherLower

🎓 Conclusion|🔝|#

  • Functional programming in modern C++ and Rust offers significant advantages in terms of code quality, maintainability, and correctness, particularly for data processing operations like file line counting. While there is a learning curve and potential performance considerations, the benefits in terms of composability, testability, and concurrency make it a valuable approach for modern software development.

  • The evolution from imperative through various functional approaches demonstrates a clear progression toward more maintainable, composable, and robust code. The modern C++23 and Rust examples show how these concepts translate to cutting-edge language features.

    • 현대 C++와 Rust의 기능 프로그래밍은 코드 품질, 유지보수 가능성, 정확성 측면에서 상당한 이점을 제공하며, 특히 파일 라인 카운팅과 같은 데이터 처리 작업에서 더욱 그렇습니다. 학습 곡선과 잠재적인 성능 고려 사항이 있지만, 구성 가능성, 테스트 가능성, 동시성 측면에서의 이점은 현대 소프트웨어 개발을 위한 귀중한 접근 방식이 됩니다.
    • 명령형 접근 방식에서 다양한 기능적 접근 방식을 통한 진화는 보다 유지 관리 가능하고, 구성 가능하며, 견고한 코드로의 명확한 발전을 보여줍니다. 최신 C++23 및 Rust 예제는 이러한 개념이 최첨단 언어 기능으로 어떻게 번역되는지 보여줍니다.

📋 Comprehensive Analysis Complete|🔝|#

  • Imperative vs Functional Programming approaches. Here’s what I’ve delivered:

📊 Core Analysis#

Comparison Table: Detailed comparison across 10 key aspects showing the fundamental differences between imperative and functional approaches.

Detailed Advantages/Disadvantages: Comprehensive analysis of both paradigms covering performance, debugging, maintainability, concurrency, and more.

🔧 Code Examples Created#

  1. Modern C++23 Functional Examples (cpp23_functional_upgrade.cpp)

    • Error handling with std::optional
    • Functional composition
    • Lazy evaluation patterns
    • Pipeline processing
  2. Rust Conversions

    • Imperative Rust (imperative_rust.rs) - Traditional approach
    • Functional Rust (functional_rust.rs) - Modern functional patterns
    • Iterator-based transformations
    • Zero-cost abstractions
  3. C++23 Modernization

    • Imperative C++23 (a01_imperative_cpp23.cpp) - Updated imperative style
    • Functional approaches with modern language features

📈 Key Insights|🔝|#

  • Imperative Programming Strengths:

    • Performance-critical operations
    • Hardware-level control
    • Easier debugging
    • Lower learning curve
  • Functional Programming Strengths:

    • Better maintainability and composability
    • Natural parallelization
    • Enhanced testability
    • Modern language feature leverage
  • 프로그래밍 강점(Imperative VS FP)

    • Imperative

      • 성능이 중요한 운영
      • 하드웨어 수준 제어
      • 더 쉬운 디버깅
      • 낮은 학습 곡선
    • (FP)기능적 프로그래밍 강점:

      • 유지 관리성 및 구성성 향상
      • 자연스러운 병렬화
      • 향상된 테스트 가능성
      • 현대 언어 기능 활용

🎯 Recommendations|🔝|#

The analysis shows that functional programming approaches offer significant advantages for data processing operations like file line counting, particularly in terms of:

  • Code Quality: Lower cyclomatic complexity, better maintainability

  • Testing: Easier to unit test with pure functions

  • Concurrency: Naturally parallelizable without shared state

  • Expressiveness: More declarative and self-documenting code

  • The progression from a01 (imperative) through a02-a07 (increasingly functional) demonstrates a clear evolution toward more robust, maintainable code.

  • All examples are compilable and demonstrate practical implementations of both paradigms in modern C++ and Rust! 🦀

Modern C++23|🔝|#

Modern C++23 Functional Examples** (cpp23_functional_upgrade.cpp)|🔝|#

// cpp23_functional_upgrade.cpp
#include <algorithm>
#include <fstream>
#include <iostream>
#include <optional>
#include <string>
#include <vector>

// Modern C++23 Functional Approach - Compatible version

// Using std::optional for error handling
std::optional<int> count_lines_safe(const std::string &filename) {
    std::ifstream in(filename);
    if (!in) {
        return std::nullopt;
    }

    std::istreambuf_iterator<char> it(in);
    std::istreambuf_iterator<char> end;

    return std::count(it, end, '\n');
}

// Functional approach using std::ranges and std::views (when available)
#ifdef __cpp_lib_ranges
std::vector<std::optional<int>>
count_lines_in_files_functional(const std::vector<std::string> &files) {
    auto to_optional = [](const std::string &file) {
        return count_lines_safe(file);
    };

    std::vector<std::optional<int>> results;
    std::ranges::transform(files, std::back_inserter(results), to_optional);
    return results;
}
#else
// Fallback for older compilers
std::vector<std::optional<int>>
count_lines_in_files_functional(const std::vector<std::string> &files) {
    std::vector<std::optional<int>> results;
    std::transform(
        files.begin(), files.end(), std::back_inserter(results),
        [](const std::string &file) { return count_lines_safe(file); });
    return results;
}
#endif

// Higher-order function approach
template <typename InputIt, typename OutputIt, typename UnaryFunc>
OutputIt transform_safe(InputIt first, InputIt last, OutputIt d_first,
                        UnaryFunc func) {
    for (auto it = first; it != last; ++it, ++d_first) {
        *d_first = func(*it);
    }
    return d_first;
}

// Lazy evaluation approach using generator pattern
class LineCounter {
  public:
    class Iterator {
      public:
        Iterator(const std::vector<std::string> *files, size_t index = 0)
            : files_(files), index_(index) {}

        Iterator &operator++() {
            ++index_;
            return *this;
        }

        std::optional<int> operator*() const {
            if (index_ < files_->size()) {
                return count_lines_safe((*files_)[index_]);
            }
            return std::nullopt;
        }

        bool operator!=(const Iterator &other) const {
            return index_ != other.index_;
        }

      private:
        const std::vector<std::string> *files_;
        size_t index_;
    };

    explicit LineCounter(const std::vector<std::string> &files)
        : files_(&files) {}

    Iterator begin() const { return Iterator(files_, 0); }
    Iterator end() const { return Iterator(files_, files_->size()); }

  private:
    const std::vector<std::string> *files_;
};

// Functional composition
template <typename F, typename G> auto compose(F f, G g) {
    return [f, g](auto &&...args) {
        return g(f(std::forward<decltype(args)>(args)...));
    };
}

// Pipeline processing
void process_files_pipeline(const std::vector<std::string> &files) {
    auto count_and_format =
        compose([](const std::string &file) { return count_lines_safe(file); },
                [](std::optional<int> count) -> std::optional<std::string> {
                    if (count) {
                        return std::to_string(*count) + " lines";
                    }
                    return std::nullopt;
                });

    std::cout << "Functional Pipeline Processing:\n";
    for (const auto &file : files) {
        auto result = count_and_format(file);
        if (result) {
            std::cout << "  " << file << ": " << *result << "\n";
        } else {
            std::cout << "  " << file << ": Error processing file\n";
        }
    }
}

int main() {
    std::cout << "C++23 Functional Programming Examples\n\n";

    const std::vector<std::string> files = {"file1.txt", "file2.txt",
                                            "file3.txt"};

    // Functional approach with error handling
    std::cout << "Functional approach with std::optional:\n";
    auto results = count_lines_in_files_functional(files);

    for (size_t i = 0; i < files.size(); ++i) {
        if (results[i]) {
            std::cout << "  " << files[i] << ": " << *results[i] << " lines\n";
        } else {
            std::cout << "  " << files[i] << ": Error - failed to count\n";
        }
    }

    // Lazy evaluation with custom iterator
    std::cout << "\nLazy evaluation with iterator pattern:\n";
    LineCounter counter(files);
    for (auto it = counter.begin(); it != counter.end(); ++it) {
        auto opt_count = *it;
        if (opt_count) {
            std::cout << "  " << *opt_count << " lines\n";
        }
    }

    // Pipeline processing
    process_files_pipeline(files);

    return 0;
}

Imperative C++23 (a01_imperative_cpp23.cpp) - Updated imperative style|🔝|#

// a01_imperative_cpp23.cpp
#include <algorithm>
#include <expected>
#include <fstream>
#include <print>
#include <ranges>
#include <string>
#include <vector>

// Modern C++23 approach using std::expected and improved ranges

// Simple line counter
class LineCounter {
  public:
    int process(const std::string &filename) const {
        std::ifstream in(filename);
        return std::count(std::istreambuf_iterator<char>(in),
                          std::istreambuf_iterator<char>(), '\n');
    }
};

// Lazy evaluation approach using ranges
auto count_lines_lazy(const std::vector<std::string> &files) {
    LineCounter counter;
    return files | std::views::transform([&counter](const auto &file) {
               return counter.process(file);
           });
}

// Modern approach with std::expected for error handling
#include <expected>
std::expected<int, std::string> count_lines_safe(const std::string &filename) {
    std::ifstream in(filename);
    if (!in) {
        return std::unexpected("Failed to open file: " + filename);
    }

    int count = std::count(std::istreambuf_iterator<char>(in),
                           std::istreambuf_iterator<char>(), '\n');

    return count;
}

// Functional approach with error handling
std::vector<std::expected<int, std::string>>
count_lines_in_files_safe(const std::vector<std::string> &files) {
    return files | std::views::transform(count_lines_safe) |
           std::ranges::to<std::vector<std::expected<int, std::string>>>();
}

// Batch processing approach
void process_batch(const std::vector<std::string> &files, size_t batch_size) {
    for (size_t i = 0; i < files.size(); i += batch_size) {
        auto end = std::min(i + batch_size, files.size());
        std::vector<std::string> batch(files.begin() + i, files.begin() + end);

        auto results = count_lines_in_files_safe(batch);

        std::print("Batch {}:\n", i / batch_size);
        for (size_t j = 0; j < batch.size(); ++j) {
            if (results[j]) {
                std::println("  {}: {} lines", batch[j], results[j].value());
            } else {
                std::println("  {}: Error - {}", batch[j], results[j].error());
            }
        }
    }
}

int main() {
    std::print("Modern C++23 Functional Programming Example\n\n");

    const std::vector<std::string> files = {"file1.txt", "file2.txt",
                                            "file3.txt"};

    // Using lazy evaluation with ranges
    std::print("Lazy evaluation with ranges:\n");
    auto lazy_results = count_lines_lazy(files);
    for (const auto &count : lazy_results) {
        std::println("  {} lines", count);
    }

    std::print("\nError handling with std::expected:\n");
    auto safe_results = count_lines_in_files_safe(files);
    for (size_t i = 0; i < files.size(); ++i) {
        if (safe_results[i]) {
            std::println("  {}: {} lines", files[i], safe_results[i].value());
        } else {
            std::println("  {}: Error - {}", files[i], safe_results[i].error());
        }
    }

    std::print("\nBatch processing:\n");
    process_batch(files, 2);

    return 0;
}

cpp23_functional_upgrade.cpp|🔝|#

// cpp23_functional_upgrade.cpp
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <optional>

// Modern C++23 Functional Approach - Compatible version

// Using std::optional for error handling
std::optional<int> count_lines_safe(const std::string& filename) {
    std::ifstream in(filename);
    if (!in) {
        return std::nullopt;
    }

    std::istreambuf_iterator<char> it(in);
    std::istreambuf_iterator<char> end;

    return std::count(it, end, '\n');
}

// Functional approach using std::ranges and std::views (when available)
#ifdef __cpp_lib_ranges
std::vector<std::optional<int>>
count_lines_in_files_functional(const std::vector<std::string>& files) {
    auto to_optional = [](const std::string& file) {
        return count_lines_safe(file);
    };

    std::vector<std::optional<int>> results;
    std::ranges::transform(files, std::back_inserter(results), to_optional);
    return results;
}
#else
// Fallback for older compilers
std::vector<std::optional<int>>
count_lines_in_files_functional(const std::vector<std::string>& files) {
    std::vector<std::optional<int>> results;
    std::transform(files.begin(), files.end(), std::back_inserter(results),
                  [](const std::string& file) {
                      return count_lines_safe(file);
                  });
    return results;
}
#endif

// Higher-order function approach
template<typename InputIt, typename OutputIt, typename UnaryFunc>
OutputIt transform_safe(InputIt first, InputIt last, OutputIt d_first, UnaryFunc func) {
    for (auto it = first; it != last; ++it, ++d_first) {
        *d_first = func(*it);
    }
    return d_first;
}

// Lazy evaluation approach using generator pattern
class LineCounter {
public:
    class Iterator {
    public:
        Iterator(const std::vector<std::string>* files, size_t index = 0)
            : files_(files), index_(index) {}

        Iterator& operator++() {
            ++index_;
            return *this;
        }

        std::optional<int> operator*() const {
            if (index_ < files_->size()) {
                return count_lines_safe((*files_)[index_]);
            }
            return std::nullopt;
        }

        bool operator!=(const Iterator& other) const {
            return index_ != other.index_;
        }

    private:
        const std::vector<std::string>* files_;
        size_t index_;
    };

    explicit LineCounter(const std::vector<std::string>& files)
        : files_(&files) {}

    Iterator begin() const { return Iterator(files_, 0); }
    Iterator end() const { return Iterator(files_, files_->size()); }

private:
    const std::vector<std::string>* files_;
};

// Functional composition
template<typename F, typename G>
auto compose(F f, G g) {
    return [f, g](auto&&... args) {
        return g(f(std::forward<decltype(args)>(args)...));
    };
}

// Pipeline processing
void process_files_pipeline(const std::vector<std::string>& files) {
    auto count_and_format = compose(
        [](const std::string& file) { return count_lines_safe(file); },
        [](std::optional<int> count) -> std::optional<std::string> {
            if (count) {
                return std::to_string(*count) + " lines";
            }
            return std::nullopt;
        }
    );

    std::cout << "Functional Pipeline Processing:\n";
    for (const auto& file : files) {
        auto result = count_and_format(file);
        if (result) {
            std::cout << "  " << file << ": " << *result << "\n";
        } else {
            std::cout << "  " << file << ": Error processing file\n";
        }
    }
}

int main() {
    std::cout << "C++23 Functional Programming Examples\n\n";

    const std::vector<std::string> files = {
        "file1.txt", "file2.txt", "file3.txt"
    };

    // Functional approach with error handling
    std::cout << "Functional approach with std::optional:\n";
    auto results = count_lines_in_files_functional(files);

    for (size_t i = 0; i < files.size(); ++i) {
        if (results[i]) {
            std::cout << "  " << files[i] << ": " << *results[i] << " lines\n";
        } else {
            std::cout << "  " << files[i] << ": Error - failed to count\n";
        }
    }

    // Lazy evaluation with custom iterator
    std::cout << "\nLazy evaluation with iterator pattern:\n";
    LineCounter counter(files);
    for (auto it = counter.begin(); it != counter.end(); ++it) {
        auto opt_count = *it;
        if (opt_count) {
            std::cout << "  " << *opt_count << " lines\n";
        }
    }

    // Pipeline processing
    process_files_pipeline(files);

    return 0;
}

Rust|🔝|#

[Imperative Rust (imperative_rust.rs) - Traditional approach]|🔝|#

// imperative_rust.rs

// Imperative Rust Approach
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

fn count_lines_imperative(filename: &str) -> Result<usize, std::io::Error> {
    let file = File::open(filename)?;
    let reader = BufReader::new(file);
    let mut count = 0;

    for line in reader.lines() {
        let _line = line?; // Explicitly handle each line
        count += 1;
    }

    Ok(count)
}

fn count_lines_in_files_imperative(files: &[&str]) -> Vec<Result<usize, std::io::Error>> {
    let mut results = Vec::with_capacity(files.len());

    for file in files {
        results.push(count_lines_imperative(file));
    }

    results
}

fn main() {
    println!("Imperative Rust Approach\n");

    let files = vec!["file1.txt", "file2.txt", "file3.txt"];
    let results = count_lines_in_files_imperative(&files);

    for (i, file) in files.iter().enumerate() {
        match &results[i] {
            Ok(count) => println!("{}: {} lines", file, count),
            Err(e) => println!("{}: Error - {}", file, e),
        }
    }
}

Functional Rust (functional_rust.rs) - Modern functional patterns|🔝|#

// functional_rust.rs
// Functional Rust Approach
use std::fs::File;
use std::io::{BufRead, BufReader};

use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

// Functional approach: Pure function with no side effects
fn count_lines_functional(filename: &str) -> Result<usize, std::io::Error> {
    let file = File::open(filename)?;
    let reader = BufReader::new(file);

    // Functional approach: Use iterator methods
    Ok(reader.lines().count())
}

// Higher-order function approach
fn count_lines_in_files_functional(files: &[&str]) -> Vec<Result<usize, std::io::Error>> {
    files
        .iter()
        .map(|&file| count_lines_functional(file))
        .collect()
}

// Even more functional: Using combinators
fn count_lines_in_files_ultra_functional(
    files: &[&str],
) -> impl Iterator<Item = Result<usize, std::io::Error>> {
    files.iter().map(|&file| count_lines_functional(file))
}

// Functional error handling with Result combinators
fn print_results_functional(files: &[&str]) {
    let results = count_lines_in_files_functional(files);

    files
        .iter()
        .zip(results.iter())
        .for_each(|(file, result)| match result {
            Ok(count) => println!("{}: {} lines", file, count),
            Err(e) => println!("{}: Error - {}", file, e),
        });
}

// Lazy evaluation approach using iterators
fn count_lines_lazy(files: &[&str]) -> impl Iterator<Item = usize> {
    files
        .iter()
        .filter_map(|&file| count_lines_functional(file).ok())
}

// Advanced functional approach: Parallel processing (using rayon)
// Note: This would require: use rayon::prelude::*;
fn count_lines_parallel(files: &[&str]) -> Vec<Result<usize, std::io::Error>> {
    files
        .par_iter() // Parallel iterator
        .map(|&file| count_lines_functional(file))
        .collect()
}

// Functional composition example
fn compose<F, G, A, B, C>(f: F, g: G) -> impl Fn(A) -> C
where
    F: Fn(A) -> B,
    G: Fn(B) -> C,
{
    move |x| g(f(x))
}

// File processing pipeline
fn process_files_with_pipeline(files: &[&str]) {
    let count_and_format = compose(
        |file: &str| count_lines_functional(file),
        |result: Result<usize, std::io::Error>| {
            result
                .map(|count| count.to_string())
                .map_err(|e| e.to_string())
        },
    );

    files.iter().for_each(|&file| match count_and_format(file) {
        Ok(formatted) => println!("{}: {} lines", file, formatted),
        Err(e) => println!("{}: Error - {}", file, e),
    });
}

fn main() {
    println!("Functional Rust Approach\n");

    let files = vec!["file1.txt", "file2.txt", "file3.txt"];

    // Basic functional approach
    let results = count_lines_in_files_functional(&files);
    println!("Basic functional approach:");
    files
        .iter()
        .zip(results.iter())
        .for_each(|(file, result)| match result {
            Ok(count) => println!("  {}: {} lines", file, count),
            Err(e) => println!("  {}: Error - {}", file, e),
        });

    // Lazy evaluation
    println!("\nLazy evaluation:");
    let lazy_counts: Vec<_> = count_lines_lazy(&files).collect();
    println!("  Successfully counted files: {:?}", lazy_counts);

    // Using print_results_functional
    println!("\nUsing functional printing:");
    print_results_functional(&files);

    // Pipeline approach
    println!("\nUsing functional pipeline:");
    process_files_with_pipeline(&files);

    // Ultra-functional approach (still lazy)
    println!("\nUltra-functional lazy iterator:");
    let ultra_results = count_lines_in_files_ultra_functional(&files);
    files
        .iter()
        .zip(ultra_results)
        .for_each(|(file, result)| match result {
            Ok(count) => println!("  {}: {} lines", file, count),
            Err(e) => println!("  {}: Error - {}", file, e),
        });

    // Parallel processing approach
    println!("\nParallel processing with rayon:");
    let parallel_results = count_lines_parallel(&files);
    files
        .iter()
        .zip(parallel_results.iter())
        .for_each(|(file, result)| match result {
            Ok(count) => println!("  {}: {} lines (parallel)", file, count),
            Err(e) => println!("  {}: Error - {} (parallel)", file, e),
        });
}
260720_Imperative_VS_FP_basic002
https://younghakim7.github.io/blog/posts/260720_imperative_vs_fp_basic002/
Author
YoungHa
Published at
2026-07-20