Functional Library

Fully utilize Topaz functional programming library. Master all functional tools including higher-order functions, currying, composition, monads, and more.

Topaz provides a powerful functional programming library to support declarative and expressive code writing. Leverage all functional tools! 🚀

🎯 Higher-Order Functions

Basic Higher-Order Functions

// Function that takes a function as argument
function applyTwice<T>(func: (T) -> T, value: T) -> T {
    return func(func(value))
}

let double = (x: int) => x * 2
let result = applyTwice(double, 5)                    // 20

// Function that returns a function
function multiplier(factor: int) -> (int) -> int {
    return (value: int) => value * factor
}

let tripleFunction = multiplier(3)
let tripleResult = tripleFunction(7)                  // 21

// Conditional function application
function conditionalApply<T>(
    condition: (T) -> bool,
    func: (T) -> T,
    value: T
) -> T {
    if condition(value) {
        return func(value)
    } else {
        return value
    }
}

let doubleIfEven = conditionalApply(
    x => x % 2 == 0,
    x => x * 2,
    8
)                                                     // 16

print("Result: {result}")
print("Triple result: {tripleResult}")
print("Double if even: {doubleIfEven}")

Function Composition

// Basic function composition
function compose<A, B, C>(
    f: (B) -> C,
    g: (A) -> B
) -> (A) -> C {
    return (input: A) => f(g(input))
}

let add1 = (x: int) => x + 1
let multiply2 = (x: int) => x * 2
let composedFunction = compose(multiply2, add1)       // (x + 1) * 2

let composedResult = composedFunction(5)              // 12

// Multiple function composition
function pipe<T>(...functions: Array<(T) -> T>) -> (T) -> T {
    return (initialValue: T) => {
        return functions.reduce((acc, currentFunc) => currentFunc(acc), initialValue)
    }
}

let complexProcessing = pipe(
    (x: int) => x + 1,        // +1
    (x: int) => x * 2,        // *2
    (x: int) => x - 3,        // -3
    (x: int) => x / 2         // /2
)

let pipeResult = complexProcessing(10)                // 9.5

print("Composed result: {composedResult}")
print("Pipe result: {pipeResult}")

Partial Application

// Partial application function
function partial<A, B, C>(
    func: (A, B) -> C,
    firstArg: A
) -> (B) -> C {
    return (secondArg: B) => func(firstArg, secondArg)
}

let add = (a: int, b: int) => a + b
let add10 = partial(add, 10)
let partialResult = add10(5)                          // 15

// Multi-argument partial application
function partial3<A, B, C, D>(
    func: (A, B, C) -> D,
    first: A,
    second: B
) -> (C) -> D {
    return (third: C) => func(first, second, third)
}

let calculate = (x: int, y: int, z: int) => x * y + z
let partialCalculate = partial3(calculate, 2, 3)      // 2 * 3 + z
let finalResult = partialCalculate(4)                 // 10

print("Partial result: {partialResult}")
print("Final result: {finalResult}")

🔗 Currying

Basic Currying

// Manual currying
function curry2<A, B, C>(func: (A, B) -> C) -> (A) -> (B) -> C {
    return (a: A) => (b: B) => func(a, b)
}

function curry3<A, B, C, D>(func: (A, B, C) -> D) -> (A) -> (B) -> (C) -> D {
    return (a: A) => (b: B) => (c: C) => func(a, b, c)
}

// Apply currying
let add = (x: int, y: int) => x + y
let curriedAdd = curry2(add)
let add5 = curriedAdd(5)
let curryResult = add5(3)                             // 8

// Three-argument currying
let formula = (x: int, y: int, z: int) => x * y + z
let curriedFormula = curry3(formula)
let stepByStepCalc = curriedFormula(2)(3)(4)          // 10

print("Curry result: {curryResult}")
print("Step by step calc: {stepByStepCalc}")

Auto Currying

// Auto currying decorator
function autoCurry<T>(func: Function) -> Function {
    // Check argument count at runtime and auto-curry
    return function(...args: Array<T>) {
        if args.length >= func.arity {
            return func.apply(null, args)
        } else {
            return function(...additionalArgs: Array<T>) {
                return autoCurry(func).apply(null, [...args, ...additionalArgs])
            }
        }
    }
}

// Usage example
let multiply = autoCurry((a: int, b: int, c: int) => a * b * c)

let method1 = multiply(2, 3, 4)                       // 24 (all args at once)
let method2 = multiply(2)(3)(4)                       // 24 (sequential application)
let method3 = multiply(2, 3)(4)                       // 24 (partial application)

let partialMultiply = multiply(2, 3)                  // Returns function
let completeMultiply = partialMultiply(4)             // 24

print("Auto curry results: {method1}, {method2}, {method3}")

🔄 Function Transformation Utilities

Memoization

// Basic memoization
function memoize<T, R>(func: (T) -> R) -> (T) -> R {
    let mut cache: Map<T, R> = Map.new()
    
    return (input: T) => {
        if cache.has(input) {
            return cache.get(input).unwrap()
        }
        
        let result = func(input)
        cache.set(input, result)
        return result
    }
}

// Fibonacci memoization
let fibonacci = memoize((n: int) => {
    print("Computing: fibonacci({n})")
    if n <= 1 { return n }
    return fibonacci(n - 1) + fibonacci(n - 2)
})

let fib10 = fibonacci(10)                             // 55 (no duplicate calculations)
let fib10Again = fibonacci(10)                        // 55 (returned from cache immediately)

// TTL memoization (Time To Live)
function memoizeWithTTL<T, R>(
    func: (T) -> R,
    ttl: int
) -> (T) -> R {
    let mut cache: Map<T, { result: R, timestamp: int }> = Map.new()
    
    return (input: T) => {
        let currentTime = Date.now()
        
        if cache.has(input) {
            let cacheEntry = cache.get(input).unwrap()
            if (currentTime - cacheEntry.timestamp) < ttl {
                return cacheEntry.result
            }
        }
        
        let result = func(input)
        cache.set(input, { result, timestamp: currentTime })
        return result
    }
}

print("Fibonacci 10: {fib10}")

Debouncing and Throttling

// Debounce function
function debounce<T>(
    func: (...T) -> void,
    delay: int
) -> (...T) -> void {
    let mut timerId: Option<int> = None
    
    return (...args: Array<T>) => {
        // Cancel existing timer
        match timerId {
            case Some(id) => clearTimeout(id)
            case None => {}
        }
        
        // Set new timer
        timerId = Some(setTimeout(() => {
            func(...args)
            timerId = None
        }, delay))
    }
}

// Throttle function
function throttle<T>(
    func: (...T) -> void,
    interval: int
) -> (...T) -> void {
    let mut lastCallTime = 0
    let mut timerId: Option<int> = None
    
    return (...args: Array<T>) => {
        let currentTime = Date.now()
        let timeSinceLastCall = currentTime - lastCallTime
        
        if timeSinceLastCall >= interval {
            func(...args)
            lastCallTime = currentTime
        } else if timerId.isNone() {
            timerId = Some(setTimeout(() => {
                func(...args)
                lastCallTime = Date.now()
                timerId = None
            }, interval - timeSinceLastCall))
        }
    }
}

// Usage examples
let searchFunction = (query: string) => {
    print("Executing search: {query}")
}

let debouncedSearch = debounce(searchFunction, 300)   // Execute after 300ms
let throttledSearch = throttle(searchFunction, 1000)  // Maximum 1 call per second

🎭 Functional Data Transformation

Lenses

// Basic lens implementation
struct Lens<S, A> {
    get: (S) -> A,
    set: (A, S) -> S
}

impl<S, A> Lens<S, A> {
    function new(getter: (S) -> A, setter: (A, S) -> S) -> Lens<S, A> {
        return Lens { get: getter, set: setter }
    }
    
    function compose<B>(other: Lens<A, B>) -> Lens<S, B> {
        return Lens.new(
            (s: S) => other.get(self.get(s)),
            (b: B, s: S) => {
                let a = self.get(s)
                let newA = other.set(b, a)
                return self.set(newA, s)
            }
        )
    }
    
    function over(func: (A) -> A, struct: S) -> S {
        let currentValue = self.get(struct)
        let newValue = func(currentValue)
        return self.set(newValue, struct)
    }
}

// User struct
struct User {
    name: string,
    age: int,
    address: Address
}

struct Address {
    city: string,
    zipCode: string
}

// Lens definitions
let nameLens = Lens.new(
    (user: User) => user.name,
    (name: string, user: User) => user{ name }
)

let addressLens = Lens.new(
    (user: User) => user.address,
    (address: Address, user: User) => user{ address }
)

let cityLens = Lens.new(
    (addr: Address) => addr.city,
    (city: string, addr: Address) => addr{ city }
)

// Lens composition
let userCityLens = addressLens.compose(cityLens)

// Usage example
let user = User {
    name: "John Doe",
    age: 30,
    address: Address { city: "New York", zipCode: "12345" }
}

let newUser = userCityLens.set("Boston", user)
let modifiedUser = nameLens.over(name => name.toUpperCase(), newUser)

print("Original city: {userCityLens.get(user)}")      // "New York"
print("New city: {userCityLens.get(newUser)}")        // "Boston"

Functional Data Pipeline

// Data transformation pipeline
struct Pipeline<T> {
    data: T
}

impl<T> Pipeline<T> {
    function from(data: T) -> Pipeline<T> {
        return Pipeline { data }
    }
    
    function map<U>(transform: (T) -> U) -> Pipeline<U> {
        return Pipeline.from(transform(self.data))
    }
    
    function filter(condition: (T) -> bool) -> Pipeline<Option<T>> {
        if condition(self.data) {
            return Pipeline.from(Some(self.data))
        } else {
            return Pipeline.from(None)
        }
    }
    
    function tap(sideEffect: (T) -> void) -> Pipeline<T> {
        sideEffect(self.data)
        return self
    }
    
    function getValue() -> T {
        return self.data
    }
}

// Data processing example
let result = Pipeline.from("  Hello World  ")
    .map(s => s.trim())
    .tap(s => print("After trim: '{s}'"))
    .map(s => s.toLowerCase())
    .tap(s => print("Lowercase: '{s}'"))
    .map(s => s.split(" "))
    .map(arr => arr.join("-"))
    .getValue()                                       // "hello-world"

print("Final result: {result}")

🏗️ Monads

Maybe/Option Monad

// Option helpers expressed as namespaced functions
module Option {
    function map<T, U>(option: Option<T>, transform: (T) -> U) -> Option<U> {
        match option {
            case Some(value) => Some(transform(value))
            case None => None
        }
    }

    function flatMap<T, U>(option: Option<T>, func: (T) -> Option<U>) -> Option<U> {
        match option {
            case Some(value) => func(value)
            case None => None
        }
    }

    function filter<T>(option: Option<T>, condition: (T) -> bool) -> Option<T> {
        match option {
            case Some(value) if condition(value) => Some(value)
            case _ => None
        }
    }

    function orElse<T>(option: Option<T>, alternative: () -> Option<T>) -> Option<T> {
        match option {
            case Some(_) => option
            case None => alternative()
        }
    }
}

// Chaining example
let userData = Some("John Doe")

let processedResult = userData
    |> Option.filter(_, name => name.length > 2)
    |> Option.map(_, name => name.toUpperCase())
    |> Option.flatMap(_, name => {
        if name.startsWith("JOHN") {
            return Some("Greeting: Hello {name}")
        } else {
            return None
        }
    })
    |> Option.orElse(_, () => Some("Unknown user"))

print("Processed result: {processedResult}")          // Some("Greeting: Hello JOHN DOE")

Result Monad

// Result monad extension
impl<T, E> Result<T, E> {
    function flatMap<U>(func: (T) -> Result<U, E>) -> Result<U, E> {
        match self {
            case Ok(value) => func(value)
            case Err(error) => Err(error)
        }
    }
    
    function mapError<F>(func: (E) -> F) -> Result<T, F> {
        match self {
            case Ok(value) => Ok(value)
            case Err(error) => Err(func(error))
        }
    }
    
    function recover(recoveryFunc: (E) -> T) -> T {
        match self {
            case Ok(value) => value
            case Err(error) => recoveryFunc(error)
        }
    }
}

// Safe operation chaining
function safeDivide(numerator: float, denominator: float) -> Result<float, string> {
    if denominator == 0.0 {
        return Err("Cannot divide by zero")
    }
    return Ok(numerator / denominator)
}

function safeSquareRoot(value: float) -> Result<float, string> {
    if value < 0.0 {
        return Err("Cannot calculate square root of negative number")
    }
    return Ok(Math.sqrt(value))
}

let calculationResult = Ok(16.0)
    .flatMap(value => safeDivide(value, 4.0))          // 16 / 4 = 4
    .flatMap(value => safeSquareRoot(value))           // √4 = 2
    .map(value => value * 2)                           // 2 * 2 = 4
    .recover(error => {
        print("Error occurred: {error}")
        return 0.0
    })

print("Calculation result: {calculationResult}")      // 4.0

🎨 Functional Patterns

Strategy Pattern

// Functional strategy pattern
enum SortStrategy {
    QuickSort,
    MergeSort,
    BubbleSort
}

function sortWith<T>(
    array: Array<T>,
    strategy: SortStrategy,
    compareFunc: (T, T) -> int
) -> Array<T> {
    let sortFunc = match strategy {
        case QuickSort => quickSort
        case MergeSort => mergeSort
        case BubbleSort => bubbleSort
    }
    
    return sortFunc(array, compareFunc)
}

function quickSort<T>(array: Array<T>, compare: (T, T) -> int) -> Array<T> {
    // QuickSort implementation
    if array.length <= 1 { return array }
    
    let pivot = array[array.length / 2]
    let smaller = array.filter(x => compare(x, pivot) < 0)
    let equal = array.filter(x => compare(x, pivot) == 0)
    let larger = array.filter(x => compare(x, pivot) > 0)
    
    return [
        ...quickSort(smaller, compare),
        ...equal,
        ...quickSort(larger, compare)
    ]
}

// Usage example
let numberArray = [64, 34, 25, 12, 22, 11, 90]
let sortedArray = sortWith(
    numberArray,
    QuickSort,
    (a, b) => a - b
)

print("Sorted array: {sortedArray}")

Observer Pattern

// Functional observer pattern
struct Observable<T> {
    subscribers: Array<(T) -> void>
}

impl<T> Observable<T> {
    function new() -> Observable<T> {
        return Observable { subscribers: [] }
    }
    
    function subscribe(callback: (T) -> void) -> () -> void {
        self.subscribers.push(callback)
        
        // Return unsubscribe function
        return () => {
            self.subscribers = self.subscribers.filter(s => s != callback)
        }
    }
    
    function next(value: T) {
        self.subscribers.forEach(callback => callback(value))
    }
    
    function map<U>(transform: (T) -> U) -> Observable<U> {
        let newObservable = Observable.new()
        
        self.subscribe(value => {
            newObservable.next(transform(value))
        })
        
        return newObservable
    }
    
    function filter(condition: (T) -> bool) -> Observable<T> {
        let newObservable = Observable.new()
        
        self.subscribe(value => {
            if condition(value) {
                newObservable.next(value)
            }
        })
        
        return newObservable
    }
}

// Usage example
let clickStream = Observable.new()

let unsubscribe1 = clickStream
    .filter(event => event.type == "click")
    .map(event => event.coordinates)
    .subscribe(coords => print("Click position: {coords}"))

let unsubscribe2 = clickStream
    .filter(event => event.doubleClick)
    .subscribe(event => print("Double click detected"))

// Emit events
clickStream.next({ type: "click", coordinates: [100, 200], doubleClick: false })
clickStream.next({ type: "click", coordinates: [300, 400], doubleClick: true })

// Later unsubscribe
unsubscribe1()
unsubscribe2()

🚀 Advanced Functional Techniques

Y Combinator

// Y combinator for recursive function implementation
function Y<T>(func: ((T) -> T) -> (T) -> T) -> (T) -> T {
    return function(arg: T) -> T {
        return func(Y(func))(arg)
    }
}

// Factorial using Y combinator
let factorial = Y((recursiveFunc: (int) -> int) => (n: int) => {
    if n <= 1 {
        return 1
    } else {
        return n * recursiveFunc(n - 1)
    }
})

let factorial5 = factorial(5)                         // 120

// Fibonacci using Y combinator
let fibonacciY = Y((recursiveFunc: (int) -> int) => (n: int) => {
    if n <= 1 {
        return n
    } else {
        return recursiveFunc(n - 1) + recursiveFunc(n - 2)
    }
})

let fib8 = fibonacciY(8)                              // 21

print("Y combinator factorial(5): {factorial5}")
print("Y combinator fibonacci(8): {fib8}")

Lazy Evaluation

// Lazy evaluation struct
struct Lazy<T> {
    computeFunc: () -> T,
    cache: Option<T>
}

impl<T> Lazy<T> {
    function new(computeFunc: () -> T) -> Lazy<T> {
        return Lazy { computeFunc, cache: None }
    }
    
    function get() -> T {
        match self.cache {
            case Some(value) => value
            case None => {
                let computedValue = self.computeFunc()
                self.cache = Some(computedValue)
                return computedValue
            }
        }
    }
    
    function map<U>(transform: (T) -> U) -> Lazy<U> {
        return Lazy.new(() => transform(self.get()))
    }
}

// Infinite sequence
struct LazySequence<T> {
    generator: (int) -> T
}

impl<T> LazySequence<T> {
    function new(generator: (int) -> T) -> LazySequence<T> {
        return LazySequence { generator }
    }
    
    function take(count: int) -> Array<T> {
        let mut result: Array<T> = []
        for i in 0..count {
            result.push(self.generator(i))
        }
        return result
    }
    
    function map<U>(transform: (T) -> U) -> LazySequence<U> {
        return LazySequence.new(index => transform(self.generator(index)))
    }
    
    function filter(condition: (T) -> bool) -> LazySequence<T> {
        return LazySequence.new(index => {
            let mut currentIndex = index
            let mut foundCount = 0
            
            while true {
                let value = self.generator(currentIndex)
                if condition(value) {
                    if foundCount == index {
                        return value
                    }
                    foundCount += 1
                }
                currentIndex += 1
            }
        })
    }
}

// Natural numbers infinite sequence
let naturalNumbers = LazySequence.new(i => i + 1)
let first10Naturals = naturalNumbers.take(10)         // [1, 2, 3, ..., 10]

// Extract even numbers only
let evenSequence = naturalNumbers.filter(x => x % 2 == 0)
let first5Evens = evenSequence.take(5)                // [2, 4, 6, 8, 10]

// Square sequence
let squareSequence = naturalNumbers.map(x => x * x)
let first5Squares = squareSequence.take(5)            // [1, 4, 9, 16, 25]

print("First 10 naturals: {first10Naturals}")
print("First 5 evens: {first5Evens}")
print("First 5 squares: {first5Squares}")

Topaz functional library enables declarative and expressive programming. Use higher-order functions, monads, lazy evaluation, and more to write more elegant and safe code! ✨