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 with explicit helper functions
struct Lens<S, A> {
get: (S) -> A,
set: (A, S) -> S
}
function createLens<S, A>(getter: (S) -> A, setter: (A, S) -> S) -> Lens<S, A> {
return Lens { get: getter, set: setter }
}
function composeLens<S, A, B>(outer: Lens<S, A>, inner: Lens<A, B>) -> Lens<S, B> {
return createLens(
(value: S) => inner.get(outer.get(value)),
(replacement: B, value: S) => {
let focus = outer.get(value)
let nextFocus = inner.set(replacement, focus)
return outer.set(nextFocus, value)
}
)
}
function overLens<S, A>(lens: Lens<S, A>, transform: (A) -> A, value: S) -> S {
let currentValue = lens.get(value)
return lens.set(transform(currentValue), value)
}
// User struct
struct User {
name: string,
age: int,
address: Address
}
struct Address {
city: string,
zipCode: string
}
// Lens definitions
let nameLens = createLens(
(user: User) => user.name,
(name: string, user: User) => user{ name }
)
let addressLens = createLens(
(user: User) => user.address,
(address: Address, user: User) => user{ address }
)
let cityLens = createLens(
(addr: Address) => addr.city,
(city: string, addr: Address) => addr{ city }
)
// Lens composition
let userCityLens = composeLens(addressLens, 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 = overLens(nameLens, name => name.toUpperCase(), newUser)
print("Original city: {userCityLens.get(user)}") // "New York"
print("New city: {userCityLens.get(newUser)}") // "Boston"
print("Updated name: {modifiedUser.name}") // "JOHN DOE"
Functional Data Pipeline
// Data transformation pipeline
struct Pipeline<T> {
data: T
}
function pipelineFrom<T>(data: T) -> Pipeline<T> {
return Pipeline { data }
}
function pipelineMap<T, U>(pipeline: Pipeline<T>, transform: (T) -> U) -> Pipeline<U> {
return Pipeline { data: transform(pipeline.data) }
}
function pipelineFilter<T>(pipeline: Pipeline<T>, condition: (T) -> bool) -> Pipeline<Option<T>> {
if condition(pipeline.data) {
return Pipeline { data: Some(pipeline.data) }
} else {
return Pipeline { data: None }
}
}
function pipelineTap<T>(pipeline: Pipeline<T>, sideEffect: (T) -> void) -> Pipeline<T> {
sideEffect(pipeline.data)
return pipeline
}
function pipelineValue<T>(pipeline: Pipeline<T>) -> T {
return pipeline.data
}
// Data processing example
let result = pipelineFrom(" Hello World ")
|> pipelineMap(_, s => s.trim())
|> pipelineTap(_, s => print("After trim: '{s}'"))
|> pipelineMap(_, s => s.toLowerCase())
|> pipelineTap(_, s => print("Lowercase: '{s}'"))
|> pipelineMap(_, s => s.split(" "))
|> pipelineMap(_, arr => arr.join("-"))
|> pipelineValue(_) // "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 helpers expressed as namespaced functions
module ResultOps {
function map<T, U, E>(result: Result<T, E>, transform: (T) -> U) -> Result<U, E> {
match result {
case Ok(value) => Ok(transform(value))
case Err(error) => Err(error)
}
}
function flatMap<T, U, E>(result: Result<T, E>, func: (T) -> Result<U, E>) -> Result<U, E> {
match result {
case Ok(value) => func(value)
case Err(error) => Err(error)
}
}
function mapError<T, E, F>(result: Result<T, E>, func: (E) -> F) -> Result<T, F> {
match result {
case Ok(value) => Ok(value)
case Err(error) => Err(func(error))
}
}
function recover<T, E>(result: Result<T, E>, recoveryFunc: (E) -> T) -> T {
match result {
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)
|> ResultOps.flatMap(_, value => safeDivide(value, 4.0)) // 16 / 4 = 4
|> ResultOps.flatMap(_, value => safeSquareRoot(value)) // √4 = 2
|> ResultOps.map(_, value => value * 2) // 2 * 2 = 4
|> ResultOps.mapError(_, error => "Calculation failed: {error}")
|> ResultOps.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>
}
function createObservable<T>() -> Observable<T> {
return Observable { subscribers: [] }
}
function subscribeObservable<T>(observable: Observable<T>, callback: (T) -> void) -> Observable<T> {
return observable{ subscribers: [...observable.subscribers, callback] }
}
function emitObservable<T>(observable: Observable<T>, value: T) {
observable.subscribers.forEach(callback => callback(value))
}
function mapObserver<T, U>(transform: (T) -> U, callback: (U) -> void) -> (T) -> void {
return value => callback(transform(value))
}
function filterObserver<T>(condition: (T) -> bool, callback: (T) -> void) -> (T) -> void {
return value => {
if condition(value) {
callback(value)
}
}
}
// Usage example
let clickPositionObserver = filterObserver(
event => event.type == "click",
mapObserver(
event => event.coordinates,
coords => print("Click position: {coords}")
)
)
let doubleClickObserver = filterObserver(
event => event.doubleClick,
event => print("Double click detected")
)
let clickStream0 = createObservable()
let clickStream1 = subscribeObservable(clickStream0, clickPositionObserver)
let clickStream2 = subscribeObservable(clickStream1, doubleClickObserver)
// Emit events
emitObservable(clickStream2, { type: "click", coordinates: [100, 200], doubleClick: false })
emitObservable(clickStream2, { type: "click", coordinates: [300, 400], doubleClick: true })
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 with explicit state transitions
struct Lazy<T> {
computeFunc: () -> T,
cache: Option<T>
}
function createLazy<T>(computeFunc: () -> T) -> Lazy<T> {
return Lazy { computeFunc, cache: None }
}
function forceLazy<T>(lazy: Lazy<T>) -> { lazy: Lazy<T>, value: T } {
match lazy.cache {
case Some(value) => return { lazy, value }
case None => {
let computedValue = lazy.computeFunc()
let cachedLazy = lazy{ cache: Some(computedValue) }
return { lazy: cachedLazy, value: computedValue }
}
}
}
function mapLazy<T, U>(lazy: Lazy<T>, transform: (T) -> U) -> Lazy<U> {
return createLazy(() => {
let forced = forceLazy(lazy)
return transform(forced.value)
})
}
// Infinite sequence
struct LazySequence<T> {
generator: (int) -> T
}
function createLazySequence<T>(generator: (int) -> T) -> LazySequence<T> {
return LazySequence { generator }
}
function takeLazySequence<T>(sequence: LazySequence<T>, count: int) -> Array<T> {
let mut result: Array<T> = []
for i in 0..<count {
result.push(sequence.generator(i))
}
return result
}
function mapLazySequence<T, U>(sequence: LazySequence<T>, transform: (T) -> U) -> LazySequence<U> {
return createLazySequence(index => transform(sequence.generator(index)))
}
function filterLazySequence<T>(sequence: LazySequence<T>, condition: (T) -> bool) -> LazySequence<T> {
return createLazySequence(index => {
let mut currentIndex = 0
let mut foundCount = 0
while true {
let value = sequence.generator(currentIndex)
if condition(value) {
if foundCount == index {
return value
}
foundCount += 1
}
currentIndex += 1
}
})
}
// Cached lazy value
let expensiveValue0 = createLazy(() => "topaz".toUpperCase())
let firstForce = forceLazy(expensiveValue0)
let expensiveValue1 = firstForce.lazy
let secondForce = forceLazy(expensiveValue1)
let mappedLength = mapLazy(expensiveValue1, value => value.length)
let mappedForce = forceLazy(mappedLength)
print("First lazy value: {firstForce.value}") // "TOPAZ"
print("Cached lazy value: {secondForce.value}") // "TOPAZ"
print("Mapped lazy value: {mappedForce.value}") // 5
// Natural numbers infinite sequence
let naturalNumbers = createLazySequence(i => i + 1)
let first10Naturals = takeLazySequence(naturalNumbers, 10) // [1, 2, 3, ..., 10]
// Extract even numbers only
let evenSequence = filterLazySequence(naturalNumbers, x => x % 2 == 0)
let first5Evens = takeLazySequence(evenSequence, 5) // [2, 4, 6, 8, 10]
// Square sequence
let squareSequence = mapLazySequence(naturalNumbers, x => x * x)
let first5Squares = takeLazySequence(squareSequence, 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!