about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xperl/functional/main.pl227
1 files changed, 227 insertions, 0 deletions
diff --git a/perl/functional/main.pl b/perl/functional/main.pl
new file mode 100755
index 0000000..2198cf3
--- /dev/null
+++ b/perl/functional/main.pl
@@ -0,0 +1,227 @@
+#!/usr/bin/env perl
+
+# -----------------------------------------------------------------------------
+#
+# main.pl - A Functional Programming Starting Point for Perl Projects
+#
+# -----------------------------------------------------------------------------
+# This script serves as a template for new Perl projects, emphasizing a
+# functional, pragmatic style. It includes standard setup, custom functional
+# utilities, and an architectural pattern for separating pure logic from
+
+# impure I/O, inspired by The Elm Architecture.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# SECTION 1: STANDARD SETUP (PRAGMAS)
+# -----------------------------------------------------------------------------
+# These pragmas enforce modern, safer Perl conventions.
+#
+# - strict:   Enforces rules to prevent common mistakes, like typos in variable
+#             names. It requires you to declare variables with `my`.
+# - warnings: Catches potential problems and questionable coding practices,
+#             printing warnings to STDERR.
+# - feature:  Enables modern language features. 'say' is a newline-aware
+#             print. 'signatures' provides clean subroutine argument lists.
+# -----------------------------------------------------------------------------
+use strict;
+use warnings;
+use feature qw(say signatures);
+
+# List::Util is a core module (no extra install needed). It provides highly
+# optimized list-processing functions. We use `reduce` (also known as `foldl`)
+# as a fundamental building block for other functions.
+use List::Util 'reduce';
+
+# For demonstrating immutability on complex data structures.
+# In a real project, you would `cpanm Readonly` to install this.
+# For this example, we will just use convention.
+# use Readonly; # Uncomment if you have the Readonly module installed.
+
+
+# -----------------------------------------------------------------------------
+# SECTION 2: FUNCTIONAL UTILITIES
+# -----------------------------------------------------------------------------
+# These are custom-built, higher-order functions that form the core of a
+# functional toolkit. They operate on other functions to create new, more
+# powerful functions.
+# -----------------------------------------------------------------------------
+
+#
+# compose(@funcs)
+#
+# Takes a list of functions and returns a new function that applies them
+# right-to-left.
+#
+# Example: compose($f, $g, $h)->($x) is equivalent to $f($g($h($x))).
+#
+# This is useful for building complex logic by chaining together simple,
+# reusable functions in a readable, mathematical style.
+#
+sub compose(@funcs) {
+    return sub ($initial_value) {
+        return reduce { $a->($b) } $initial_value, reverse @funcs;
+    };
+}
+
+#
+# pipe(@funcs)
+#
+# Takes a list of functions and returns a new function that applies them
+# left-to-right. This is often more intuitive than compose for data
+# processing workflows.
+#
+# Example: pipe($f, $g, $h)->($x) is equivalent to $h($g($f($x))).
+#
+# This reads like a sequence of steps, e.g., "take x, then do f, then do g".
+#
+sub pipe(@funcs) {
+    return sub ($initial_value) {
+        return reduce { $b->($a) } $initial_value, @funcs;
+    };
+}
+
+#
+# curry($func, @args)
+#
+# Transforms a function that takes multiple arguments into a function that
+# takes some of those arguments now, and the rest later.
+#
+# Example:
+#   my $add = sub ($x, $y) { $x + $y };
+#   my $add_five = curry($add, 5); # Creates a new function that adds 5.
+#   my $result = $add_five->(10);  # Result is 15.
+#
+# This is excellent for creating specialized versions of general functions.
+#
+sub curry($func, @args) {
+    return sub {
+        return $func->(@args, @_);
+    };
+}
+
+# -----------------------------------------------------------------------------
+# SECTION 3: ARCHITECTURE - PURE LOGIC VS IMPURE I/O
+# -----------------------------------------------------------------------------
+# This section demonstrates a pattern for structuring applications to achieve
+# a clean separation of concerns. All core logic is contained in a single,
+# pure `update` function. All I/O (printing, reading, etc.) is handled
+# outside of it. This makes the core logic extremely easy to test and reason
+# about.
+#
+# This pattern is a simplified version of The Elm Architecture.
+#   - Model:  The state of your application.
+#   - Msg:    A description of something that happened (e.g., user input).
+#   - update: A pure function that computes the new state.
+# -----------------------------------------------------------------------------
+
+#
+# update($msg, $model)
+#
+# This is the PURE core of the application.
+# It takes a message (describing an event) and the current state (the model),
+# and it returns the NEW state.
+#
+# Crucially, this function does NO I/O. It doesn't print, read files, or have
+# any other "side effects". Its only job is to compute the next state.
+# Because it's pure, you can call it with the same arguments 100 times and
+# always get the same result, making it perfectly predictable and testable.
+#
+sub update($msg, $model) {
+    # Create a mutable copy to work with. The original $model is not changed.
+    my %new_model = %{$model};
+
+    if ($msg->{type} eq 'INCREMENT') {
+        $new_model{counter} += 1;
+    }
+    elsif ($msg->{type} eq 'ADD_X') {
+        $new_model{counter} += $msg->{payload};
+    }
+    elsif ($msg->{type} eq 'SET_NAME') {
+        $new_model{name} = $msg->{payload};
+    }
+
+    # Return a reference to the new model.
+    # Note: A more robust implementation would ensure a deep, immutable copy.
+    # For many scripts, convention is sufficient.
+    return \%new_model;
+}
+
+
+# -----------------------------------------------------------------------------
+# SECTION 4: MAIN EXECUTION BLOCK (The "IMPURE" Runtime)
+# -----------------------------------------------------------------------------
+# This is the imperative shell that orchestrates the application. It handles
+# all I/O and calls the pure `update` function to process state changes.
+# -----------------------------------------------------------------------------
+sub main() {
+    say "--- Functional Perl Starter ---";
+
+    # --- Part 1: Demonstrating Built-in Functional Operators ---
+    say "\n1. Demonstrating map and reduce (from List::Util)";
+    my @numbers = (1 .. 5);
+
+    # `map` applies a function to each element of a list, returning a new list.
+    my @doubled = map { $_ * 2 } @numbers;
+    say "Original: @numbers";
+    say "Doubled:  @doubled";
+
+    # `reduce` combines all elements of a list into a single value.
+    my $sum = reduce { $a + $b } 0, @numbers; # Start with an accumulator of 0
+    say "Sum:      $sum";
+
+
+    # --- Part 2: Demonstrating Custom Functional Utilities ---
+    say "\n2. Demonstrating pipe, compose, and curry";
+
+    my $add_one   = sub ($n) { $n + 1 };
+    my $mult_two  = sub ($n) { $n * 2 };
+    my $sub_three = sub ($n) { $n - 3 };
+
+    # pipe: (5 + 1) * 2 - 3 = 9
+    my $pipeline_op = pipe($add_one, $mult_two, $sub_three);
+    say "pipe(5): " . $pipeline_op->(5);
+
+    # compose: 5 * 2 + 1 - 3 = 8
+    my $compose_op = compose($sub_three, $add_one, $mult_two);
+    say "compose(5): " . $compose_op->(5);
+
+    # curry
+    my $add = sub ($x, $y) { $x + $y };
+    my $add_ten = curry($add, 10);
+    say "curry(add 10 to 7): " . $add_ten->(7); # 17
+
+
+    # --- Part 3: Demonstrating the Pure/Impure Architecture ---
+    say "\n3. Demonstrating The Elm-style Architecture";
+
+    # The initial state of our application.
+    my $model = {
+        counter => 0,
+        name    => "World",
+    };
+
+    say "Initial Model: counter = $model->{counter}, name = $model->{name}";
+
+    # We simulate a sequence of events (messages).
+    my @events = (
+        { type => 'INCREMENT' },
+        { type => 'INCREMENT' },
+        { type => 'SET_NAME', payload => 'Perl' },
+        { type => 'ADD_X',    payload => 10 },
+    );
+
+    # The "Runtime Loop": Process each event through the pure `update` function.
+    # The runtime's job is to call `update` and then perform I/O (like `say`).
+    for my $msg (@events) {
+        say "  -> Processing event: $msg->{type}";
+        $model = update($msg, $model); # Calculate new state
+    }
+
+    say "Final Model:   counter = $model->{counter}, name = $model->{name}";
+
+    return 0;
+}
+
+# Run the main subroutine.
+exit main();