Topaz Docs

Functions and Processes in Topaz

In Topaz, functions are more accurately referred to as Processes. They are the primary building blocks for logic and abstraction. A core tenet of the language is that processes are first-class citizens, a principle that grants them incredible power and flexibility by deeply embedding functional programming concepts into the language.

Defining Processes

Processes are typically created as anonymous functions using the λ (lambda) special form. To give a process a name, you bind this anonymous process to an identifier using the $ special form.

Syntax:

($ PROCESS_NAME
  (λ (PARAM_1 PARAM_2 ...)
    ;; Body expression(s) go here.
    ;; The result of the last expression is returned.
  )
)

Example:

;; A simple process that adds two numbers
($ add
  (λ (x y)
    (+ x y)
  )
)

;; Calling the process is like any other operation
(add 5 10) ;; Evaluates to 15

First-Class Citizens

Being "first-class" means processes are treated like any other data value (such as a number or a string). They can be:

1. Stored in Variables/Identifiers

You can bind a process to an identifier. This allows you to refer to it by name, and even assign it to other identifiers.

($ greeting-process (λ (name) (CONCAT "Hello, " name)))
($ say-hello greeting-process) ;; Assigning the process value to a new identifier

(say-hello "Topaz") ;; Evaluates to "Hello, Topaz"

2. Passed as Arguments

You can pass processes as arguments to other processes. This is a fundamental concept for enabling powerful patterns like callbacks and higher-order functions.

;; A process that takes another process 'f' and a value 'v',
;; and applies 'f' to 'v'.
($ apply-it
  (λ (f v)
    (f v)
  )
)

($ square (λ (n) (* n n)))

;; Here, we pass the 'square' process as an argument.
(apply-it square 10) ;; Evaluates to 100

;; Generic higher-order function example
($ compose
  (λ [A B C] (f: (Process B C) g: (Process A B))
    (λ (x: A)
      (f (g x))
    )
  )
)

3. Returned from Other Processes

A process can create and return another, often specialized, process.

;; This process doesn't just return a value; it returns a new process.
($ make-adder
  (λ (amount-to-add)
    ;; This inner lambda is what gets returned.
    (λ (n)
      (+ n amount-to-add)
    )
  )
)

;; Create specialized versions of the adder.
($ add-five (make-adder 5))
($ add-ten (make-adder 10))

(add-five 100) ;; Evaluates to 105
(add-ten 100)  ;; Evaluates to 110

This powerful pattern utilizes Closures, where the returned inner process "remembers" the amount-to-add value from the environment in which it was created.

Recursion

In Topaz, as in many functional languages, iteration is most idiomatically achieved through recursion, rather than traditional for or while loops. This approach avoids side effects and state mutations, aligning with the principle of immutability.

;; A recursive process to calculate the factorial of a number.
($ factorial
  (λ (n)
    (IF (<= n 1)
      1 ;; Base case: factorial of 0 or 1 is 1.
      (* n (factorial (- n 1))) ;; Recursive step: n * factorial(n-1).
    )
  )
)

(factorial 5) ;; Evaluates to 120 (which is 5 * 4 * 3 * 2 * 1)

;; Tail-recursive version for better performance
($ factorial-tail
  (λ (n)
    ($ factorial-helper
      (λ (n acc)
        (IF (<= n 1)
          acc
          (factorial-helper (- n 1) (* n acc))
        )
      )
    )
    (factorial-helper n 1)
  )
)

Generic and Higher-Order Processes

Topaz v1.0 supports both generic type parameters and higher-order functions, enabling powerful abstractions.

Generic Processes

Generic processes can work with multiple types by declaring type parameters:

;; A generic identity function
($ identity
  (λ [T] (value: T)
    value
  )
)

;; A generic map function for vectors
($ vector-map
  (λ [A B] (f: (Process A B) vec: (Vector A))
    ;; Implementation would be provided by the standard library
    (vector:map f vec)
  )
)

;; Usage with type inference
($ numbers [1 2 3 4])
($ doubled (vector-map (λ (x) (* x 2)) numbers))  ;; A=Integer, B=Integer inferred

Currying and Partial Application

Topaz naturally supports currying through its lambda syntax:

;; A curried addition function
($ add-curried
  (λ (x)
    (λ (y)
      (+ x y)
    )
  )
)

;; Create specialized functions through partial application
($ add-five (add-curried 5))
($ add-ten (add-curried 10))

(io:print (core:to-string (add-five 7)))   ;; Prints "12"
(io:print (core:to-string (add-ten 20)))   ;; Prints "30"