# Advanced Combinators ## What are Advanced Combinators? Advanced combinators are powerful patterns that combine multiple functions and operations to solve complex problems. They build on the basic combinators you've already learned. ## Partial Application and Currying ### Creating Specialized Functions ```plaintext /* Basic partial application */ add : x y -> x + y; add_ten : add 10; result : add_ten 5; /* 15 */ /* Complex partial application */ format_with_prefix : prefix value -> prefix + ": " + value; format_name : format_with_prefix "Name"; format_age : format_with_prefix "Age"; person : {name: "Alice", age: 30}; formatted_name : format_name person.name; /* "Name: Alice" */ formatted_age : format_age person.age; /* "Age: 30" */ ``` ### Currying with Combinators ```plaintext /* Create specialized functions */ multiply_by : x y -> x * y; double : multiply_by 2; triple : multiply_by 3; numbers : {1, 2, 3, 4, 5}; doubled : map @double numbers; /* {2, 4, 6, 8, 10} */ tripled : map @triple numbers; /* {3, 6, 9, 12, 15} */ ``` ## Higher-Order Combinators ### Combinators that Work with Other Combinators ```plaintext /* Apply a combinator to multiple collections */ apply_to_all : combinator collections -> reduce @t.merge {} (map @combinator collections); /* Example usage */ add_one : x -> x + 1; collections : {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; all_incremented : apply_to_all @map @add_one collections; /* Result: {1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10} */ ``` ### Composing Multiple Functions ```plaintext /* Compose many functions together */ compose_many : functions -> reduce @compose @identity functions; /* Example usage */ double_then_increment : compose @increment @double; complex_transform : compose @double_then_increment @square; result : complex_transform 3; /* Result: 19 (3^2=9, 9*2=18, 18+1=19) */ ``` ## Memoization Pattern ### Caching Function Results ```plaintext /* Simple memoization */ memoize : f -> { cache: {}, compute: x -> when t.has cache x then t.get cache x _ then { result: f x, new_cache: t.set cache x (f x) } }; /* Using memoized function */ expensive_calc : x -> x * x * x; /* Simulate expensive computation */ memoized_calc : memoize @expensive_calc; result1 : memoized_calc.compute 5; /* Computes 125 */ result2 : memoized_calc.compute 5; /* Uses cached result */ ``` ## Real-World Problem Solving ### E-commerce Order Processing ```plaintext /* Process customer orders */ orders : { order1: {customer: "Alice", items: {book: 2, pen: 5}, status: "pending"}, order2: {customer: "Bob", items: {laptop: 1}, status: "shipped"}, order3: {customer: "Charlie", items: {book: 1, pen: 3}, status: "pending"} }; prices : {book: 15, pen: 2, laptop: 800}; /* Calculate order totals */ calculate_total : order -> { customer: order.customer, total: reduce @add 0 (map @calculate_item_total order.items), status: order.status }; calculate_item_total : item quantity -> when item is "book" then 15 * quantity "pen" then 2 * quantity "laptop" then 800 * quantity _ then 0; /* Process all orders */ processed_orders : map @calculate_total orders; ..out processed_orders; ``` ### Data Transformation Pipeline ```plaintext /* Transform user data through multiple stages */ users : { alice: {name: "Alice", age: 25, city: "NYC", active: true}, bob: {name: "Bob", age: 30, city: "LA", active: false}, charlie: {name: "Charlie", age: 35, city: "NYC", active: true} }; /* Pipeline stages */ filter_active : users -> filter @is_active users; add_greeting : users -> map @add_greeting_to_user users; format_output : users -> map @format_user_output users; is_active : user -> user.active; add_greeting_to_user : user -> t.merge user {greeting: "Hello, " + user.name}; format_user_output : user -> { name: user.name, greeting: user.greeting, location: user.city }; /* Execute pipeline */ active_users : filter_active users; greeted_users : add_greeting active_users; formatted_users : format_output greeted_users; ..out formatted_users; ``` ## Advanced Patterns ### Lazy Evaluation ```plaintext /* Lazy evaluation with thunks */ lazy : computation -> { compute: computation, evaluated: false, result: null, get: -> when evaluated then result _ then { computed_result: compute, new_lazy: { compute: computation, evaluated: true, result: computed_result, get: -> computed_result } } }; /* Use lazy evaluation */ expensive_operation : -> { /* Simulate expensive computation */ ..out "Computing..."; 42 }; lazy_result : lazy expensive_operation; /* Computation hasn't happened yet */ actual_result : lazy_result.get; /* Now computation happens */ ``` ### Continuation-Passing Style ```plaintext /* Continuation-passing style for complex control flow */ process_with_continuation : data success_cont error_cont -> when data = null then error_cont "No data provided" _ then processed : transform data; when processed.error is true then error_cont processed.message false then success_cont processed.result; /* Use continuations */ success_handler : result -> ..out "Success: " + result; error_handler : error -> ..out "Error: " + error; process_with_continuation "valid data" success_handler error_handler; process_with_continuation null success_handler error_handler; ``` ## Performance Optimization ### Avoiding Redundant Computations ```plaintext /* Cache expensive computations */ expensive_transform : data -> /* Simulate expensive operation */ data * data * data; /* With caching */ transform_with_cache : { cache: {}, transform: data -> when t.has cache data then t.get cache data _ then { result: expensive_transform data, new_cache: t.set cache data (expensive_transform data) } }; /* Use cached version */ result1 : transform_with_cache.transform 5; /* Computes */ result2 : transform_with_cache.transform 5; /* Uses cache */ ``` ### Lazy Collections ```plaintext /* Lazy collection processing */ lazy_map : f collection -> { f: f, collection: collection, get: index -> when index >= t.length collection then null _ then f (t.get collection index) }; /* Use lazy mapping */ numbers : {1, 2, 3, 4, 5}; expensive_double : x -> { /* Simulate expensive operation */ ..out "Doubling " + x; x * 2 }; lazy_doubled : lazy_map @expensive_double numbers; /* No computation yet */ first_result : lazy_doubled.get 0; /* Only computes for index 0 */ ``` ## Best Practices ### Keep Combinators Focused ```plaintext /* Good: Single responsibility */ filter_by_age : min_age users -> filter @(is_older_than min_age) users; is_older_than : min_age user -> user.age >= min_age; /* Avoid: Multiple responsibilities */ bad_filter : min_age max_age users -> filter @(complex_age_check min_age max_age) users; ``` ### Use Descriptive Names ```plaintext /* Good: Clear intent */ process_active_users : users -> filter @is_active (map @add_user_id users); /* Avoid: Generic names */ process : data -> filter @check (map @transform data); ``` ### Compose, Don't Nest ```plaintext /* Good: Composed functions */ pipeline : compose @format_output (compose @add_metadata (filter @is_valid data)); /* Avoid: Deep nesting */ nested : format_output (add_metadata (filter @is_valid data)); ``` ## Next Steps Now that you understand advanced combinators, explore: - [Integration Patterns](15_Integration_Patterns.md) for external system integration - [Error Handling](13_Error_Handling.md) for robust error management - [Best Practices](16_Best_Practices.md) for writing clean code