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! ✨