---
url: 'https://sema-lang.com/docs/tutorial/functions.md'
---

# Functions and Scope

Functions are the primary building blocks of Sema programs. Functions in Sema are first-class, meaning they can be bound to variables, passed as arguments to other functions, and returned from functions.

***

## 1. Defining Named Functions

There are two equivalent ways to define a named function:

### The `define` Form

`define` binds a name to a value. Given a signature list rather than a bare symbol, it binds the name to a function — shorthand for `(define square (fn (x) (* x x)))`:

```sema
(define (square x)
  (* x x))

(square 5) ; => 25
```

This is the form used throughout Sema's examples and standard library.

### The `defun` Form

`defun` takes the name and the parameter list as separate arguments:

```sema
(defun square (x)
  (* x x))
```

`defn` is accepted as an alias for `defun`.

***

## 2. Anonymous Functions (Lambdas)

Anonymous functions are functions without a name, commonly used when passing functions to higher-order helpers like `map` or `filter`.

### Using `fn`

You can define an anonymous function using the `fn` form:

```sema
(map (fn (x) (* x x)) '(1 2 3))
; => (1 4 9)
```

### Shorthand Lambdas `#(...)`

Sema provides a compact Clojure-style shorthand syntax for short anonymous functions.

* `#(...)` creates an anonymous function.
* `%` represents the first argument (or you can use `%1`, `%2`, etc., for multiple arguments).

```sema
;; Square a number
(map #(* % %) '(1 2 3)) ; => (1 4 9)

;; Add two numbers
(define add #(+ %1 %2))
(add 10 20) ; => 30
```

***

## 3. Scope and Closures

Sema functions are **lexically scoped**. This means they can access variables declared in their outer parent scopes. When a function references variables from its enclosing scope, it creates a **closure**:

```sema
(define (make-adder x)
  (fn (y) (+ x y)))

(define add-five (make-adder 5))
(add-five 10) ; => 15
```

In the example above, `add-five` remembers the value of `x` (which is `5`) even after `make-adder` has finished execution.

***

## 4. Recursion and Tail-Call Optimization (TCO)

While Sema supports iterative forms, the standard way to perform repetitive tasks is via **recursion**.

Sema implements **Tail-Call Optimization (TCO)**. When a function calls itself (or another function) in the **tail position** (meaning the call is the absolute last action in the function), the runtime reuses the current stack frame instead of allocating a new one. This prevents stack overflow errors, regardless of how deep the recursion is.

### Example: Tail-Recursive Factorial

A function is tail-recursive if the recursive call's value is directly returned without further computation:

```sema
(define (factorial n accumulator)
  (if (<= n 1)
      accumulator
      (factorial (- n 1) (* n accumulator)))) ; Tail position

(factorial 5 1) ; => 120
```

Contrast this with a non-tail-recursive version where the recursive call is not the last operation:

```sema
(define (factorial-bad n)
  (if (<= n 1)
      1
      (* n (factorial-bad (- n 1))))) ; NOT in tail position (* must run after)
```

***

## Next Steps

Now that you know how to write functions, let's look at how Sema handles concurrency and asynchronous programming:

* [Concurrency & Async](./concurrency.md)
