Functions
Functions are first-class citizens in Zenoscript, designed with functional programming principles in mind. They support currying, partial application, composition, and other powerful functional patterns.
Function Declaration
Basic Syntax
typescript
// Arrow function with automatic return
let add = (a: number, b: number) => { a + b }
// Multi-line function with optional return
let greet = (name: string) => {
let message = `Hello, ${name}!`
message // Automatically returned
}
// Function with explicit type annotations
let multiply = (x: number, y: number): number => { x * y }
// No parameters
let getCurrentTime = () => new Date()
// Traditional syntax still works
let traditionalAdd = (a: number, b: number) => {
return a + b // Explicit return
}Optional Parentheses in Function Calls
Zenoscript supports clean, readable function calls without parentheses:
typescript
// Clean function calls
console.log "Hello, World!"
processValue 42
validateInput userData
handleResponse apiResponse
// Traditional calls still work
console.log("Hello, World!")
processValue(42)
// Multiple arguments require parentheses
add(10, 20)
createUser("Alice", 30, "alice@example.com")Automatic Return Statements
Functions return their last expression automatically:
typescript
// Single expression functions
let square = (x) => { x * x }
let isEven = (n) => { n % 2 === 0 }
let capitalize = (str) => { str.charAt(0).toUpperCase() + str.slice(1) }
// Multi-statement functions
let processData = (input) => {
let cleaned = sanitizeInput(input)
let validated = validateData(cleaned)
let transformed = transformData(validated)
transformed // Returned automatically
}
// Control flow with automatic return
let getGrade = (score) => {
if (score >= 90) {
return "A" // Explicit return for early exit
}
if (score >= 80) {
return "B"
}
"F" // Automatic return for default case
}Function Expressions
typescript
// Function as expression
let operation = (a: number, b: number) => a + b
// Function in object
let math = {
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b
}
// Function as parameter
let applyOperation = (fn: (a: number, b: number) => number, x: number, y: number) => {
return fn(x, y)
}Function Types
Type Annotations
typescript
// Function type aliases
type BinaryOperation = (a: number, b: number) => number
type Predicate<T> = (item: T) => boolean
type Mapper<T, U> = (input: T) => U
type Consumer<T> = (item: T) => void
// Using function types
let add: BinaryOperation = (a, b) => a + b
let isEven: Predicate<number> = n => n % 2 === 0
let toString: Mapper<number, string> = n => n.toString()
let log: Consumer<string> = msg => console.log(msg)Generic Functions
typescript
// Generic function
let identity = <T>(value: T): T => value
// Generic with constraints
let getProperty = <T, K extends keyof T>(obj: T, key: K): T[K] => {
return obj[key]
}
// Multiple type parameters
let combine = <T, U>(first: T, second: U): [T, U] => {
return [first, second]
}
// Generic with default type
let createArray = <T = string>(item: T, length: number): T[] => {
return new Array(length).fill(item)
}
// Usage
let nums = createArray(0, 5) // number[]
let strs = createArray("hello", 3) // string[]
let bools = createArray<boolean>(true, 2) // boolean[]Higher-Order Functions
Functions that Return Functions
typescript
// Currying
let add = (a: number) => (b: number) => a + b
let add5 = add(5)
let result = add5(3) // 8
// Function factory
let createValidator = (min: number, max: number) => {
return (value: number) => value >= min && value <= max
}
let ageValidator = createValidator(0, 120)
let isValidAge = ageValidator(25) // true
// Configuration function
let createLogger = (prefix: string) => {
return (message: string) => {
console.log(`[${prefix}] ${message}`)
}
}
let errorLogger = createLogger("ERROR")
let infoLogger = createLogger("INFO")Functions that Take Functions
typescript
// Higher-order array operations
let map = <T, U>(array: T[], fn: (item: T) => U): U[] => {
let result: U[] = []
for (let item of array) {
result.push(fn(item))
}
return result
}
let filter = <T>(array: T[], predicate: (item: T) => boolean): T[] => {
let result: T[] = []
for (let item of array) {
if (predicate(item)) {
result.push(item)
}
}
return result
}
let reduce = <T, U>(array: T[], fn: (acc: U, item: T) => U, initial: U): U => {
let result = initial
for (let item of array) {
result = fn(result, item)
}
return result
}
// Usage
let numbers = [1, 2, 3, 4, 5]
let doubled = map(numbers, x => x * 2)
let evens = filter(numbers, x => x % 2 === 0)
let sum = reduce(numbers, (acc, x) => acc + x, 0)Function Composition
Basic Composition
typescript
// Compose function
let compose = <A, B, C>(f: (b: B) => C, g: (a: A) => B) => {
return (a: A) => f(g(a))
}
// Pipe function
let pipe = <A, B, C>(g: (a: A) => B, f: (b: B) => C) => {
return (a: A) => f(g(a))
}
// Example functions
let addOne = (x: number) => x + 1
let double = (x: number) => x * 2
let toString = (x: number) => x.toString()
// Composition
let addOneThenDouble = compose(double, addOne)
let doubleThenAddOne = pipe(double, addOne)
let result1 = addOneThenDouble(5) // (5 + 1) * 2 = 12
let result2 = doubleThenAddOne(5) // (5 * 2) + 1 = 11Pipeline Operations
typescript
// Using Zenoscript's pipe operator
let processNumber = (x: number) => {
return x
|> addOne
|> double
|> toString
}
let result = processNumber(5) // "12"
// Complex pipeline
let processUsers = (users: User[]) => {
return users
|> filter(user => user.active)
|> map(user => user.name)
|> filter(name => name.length > 3)
|> sort
}Partial Application
typescript
// Partial application helper
let partial = <T extends any[], U extends any[], R>(
fn: (...args: [...T, ...U]) => R,
...partialArgs: T
) => {
return (...restArgs: U) => fn(...partialArgs, ...restArgs)
}
// Example usage
let multiply3 = (a: number, b: number, c: number) => a * b * c
let multiplyBy2And3 = partial(multiply3, 2, 3)
let result = multiplyBy2And3(4) // 2 * 3 * 4 = 24
// Practical example
let createUrl = (protocol: string, domain: string, path: string) => {
return `${protocol}://${domain}${path}`
}
let createHttpsUrl = partial(createUrl, "https")
let createApiUrl = partial(createHttpsUrl, "api.example.com")
let userUrl = createApiUrl("/users") // "https://api.example.com/users"Closures and Lexical Scope
typescript
// Closure example
let createCounter = () => {
let count = 0
return {
increment: () => ++count,
decrement: () => --count,
getValue: () => count
}
}
let counter = createCounter()
console.log(counter.increment()) // 1
console.log(counter.increment()) // 2
console.log(counter.getValue()) // 2
// Module pattern with closures
let createCalculator = () => {
let history: number[] = []
return {
add: (a: number, b: number) => {
let result = a + b
history.push(result)
return result
},
getHistory: () => [...history], // Return copy
clearHistory: () => {
history = []
}
}
}Recursion
Basic Recursion
typescript
// Factorial
let factorial = (n: number): number => {
return n <= 1 ? 1 : n * factorial(n - 1)
}
// Fibonacci
let fibonacci = (n: number): number => {
return n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)
}
// Sum of array
let sumArray = (arr: number[]): number => {
return arr.length === 0 ? 0 : arr[0] + sumArray(arr.slice(1))
}Tail Recursion
typescript
// Tail-recursive factorial
let factorialTail = (n: number, acc: number = 1): number => {
return n <= 1 ? acc : factorialTail(n - 1, n * acc)
}
// Tail-recursive sum
let sumTail = (arr: number[], acc: number = 0): number => {
return arr.length === 0 ? acc : sumTail(arr.slice(1), acc + arr[0])
}
// Tail-recursive list operations
let mapTail = <T, U>(
arr: T[],
fn: (item: T) => U,
acc: U[] = []
): U[] => {
return arr.length === 0
? acc
: mapTail(arr.slice(1), fn, [...acc, fn(arr[0])])
}Memoization
typescript
// Memoization helper
let memoize = <T extends any[], R>(fn: (...args: T) => R) => {
let cache = new Map<string, R>()
return (...args: T): R => {
let key = JSON.stringify(args)
if (cache.has(key)) {
return cache.get(key)!
}
let result = fn(...args)
cache.set(key, result)
return result
}
}
// Memoized fibonacci
let fibonacciMemo = memoize((n: number): number => {
return n <= 1 ? n : fibonacciMemo(n - 1) + fibonacciMemo(n - 2)
})
// Memoized expensive computation
let expensiveComputation = memoize((data: any[]) => {
// Simulate expensive operation
return data.reduce((acc, item) => acc + item.value, 0)
})Async Functions
Promises and Async/Await
typescript
// Async function
let fetchUser = async (id: string): Promise<User> => {
let response = await fetch(`/api/users/${id}`)
return await response.json()
}
// Error handling
let fetchUserSafe = async (id: string): Promise<User | null> => {
try {
let user = await fetchUser(id)
return user
} catch (error) {
console.error("Failed to fetch user:", error)
return null
}
}
// Async composition
let fetchAndProcessUser = async (id: string) => {
return await fetchUser(id)
|> (user => ({ ...user, processed: true }))
}Async Higher-Order Functions
typescript
// Async map
let mapAsync = async <T, U>(
array: T[],
fn: (item: T) => Promise<U>
): Promise<U[]> => {
let results: U[] = []
for (let item of array) {
results.push(await fn(item))
}
return results
}
// Async filter
let filterAsync = async <T>(
array: T[],
predicate: (item: T) => Promise<boolean>
): Promise<T[]> => {
let results: T[] = []
for (let item of array) {
if (await predicate(item)) {
results.push(item)
}
}
return results
}
// Usage
let userIds = ["1", "2", "3"]
let users = await mapAsync(userIds, fetchUser)
let activeUsers = await filterAsync(users, async user => {
return await checkUserStatus(user.id) === "active"
})Pure Functions
Characteristics of Pure Functions
typescript
// Pure function - same input, same output, no side effects
let add = (a: number, b: number) => a + b
let multiply = (x: number, y: number) => x * y
// Pure function with objects (returns new object)
let updateUser = (user: User, updates: Partial<User>): User => {
return { ...user, ...updates }
}
// Pure array operations
let addItem = <T>(array: T[], item: T): T[] => {
return [...array, item]
}
let removeItem = <T>(array: T[], index: number): T[] => {
return array.filter((_, i) => i !== index)
}Avoiding Side Effects
typescript
// Impure - modifies external state
let count = 0
let impureIncrement = () => {
count++ // Side effect
return count
}
// Pure - returns new state
let pureIncrement = (count: number) => count + 1
// Impure - modifies input
let impureSort = (array: number[]) => {
array.sort() // Mutates input
return array
}
// Pure - returns new array
let pureSort = (array: number[]) => {
return [...array].sort()
}Function Utilities
Common Utility Functions
typescript
// Debounce
let debounce = <T extends any[]>(
fn: (...args: T) => void,
delay: number
) => {
let timeoutId: number | undefined
return (...args: T) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn(...args), delay)
}
}
// Throttle
let throttle = <T extends any[]>(
fn: (...args: T) => void,
limit: number
) => {
let inThrottle = false
return (...args: T) => {
if (!inThrottle) {
fn(...args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// Once
let once = <T extends any[], R>(fn: (...args: T) => R) => {
let called = false
let result: R
return (...args: T): R => {
if (!called) {
called = true
result = fn(...args)
}
return result
}
}Best Practices
1. Prefer Pure Functions
typescript
// Good - pure function
let calculateTotal = (items: Item[]) => {
return items.reduce((sum, item) => sum + item.price, 0)
}
// Avoid - impure function
let total = 0
let calculateTotalImpure = (items: Item[]) => {
total = items.reduce((sum, item) => sum + item.price, 0)
}2. Use Function Composition
typescript
// Good - compose small functions
let processData = (data: any[]) => {
return data
|> filter(isValid)
|> map(normalize)
|> sort(byPriority)
}
// Avoid - large monolithic function
let processDataMonolithic = (data: any[]) => {
let result = []
for (let item of data) {
if (item && item.value > 0) {
let normalized = {
id: item.id,
value: item.value / 100,
priority: item.priority || 0
}
result.push(normalized)
}
}
return result.sort((a, b) => b.priority - a.priority)
}3. Use Meaningful Names
typescript
// Good - descriptive names
let isValidEmail = (email: string) => /\S+@\S+\.\S+/.test(email)
let formatCurrency = (amount: number) => `$${amount.toFixed(2)}`
// Avoid - generic names
let check = (input: string) => /\S+@\S+\.\S+/.test(input)
let format = (num: number) => `$${num.toFixed(2)}`4. Keep Functions Small
typescript
// Good - small, focused functions
let validateUser = (user: User) => {
return isValidEmail(user.email) &&
isValidAge(user.age) &&
isValidName(user.name)
}
let isValidEmail = (email: string) => /\S+@\S+\.\S+/.test(email)
let isValidAge = (age: number) => age >= 0 && age <= 120
let isValidName = (name: string) => name.length >= 2Functions in Zenoscript are designed to be expressive, composable, and safe, making it easy to build complex functionality from simple, reusable pieces.