# How to Create Custom Native Functions in Quarkdown’s Stdlib Module

> Learn to create custom native functions in Quarkdown using Kotlin. Export and register your functions easily with Stdlib.kt for extended functionality and enhanced performance.

- Repository: [Giorgio Garofalo/quarkdown](https://github.com/iamgio/quarkdown)
- Tags: how-to-guide
- Published: 2026-04-29

---

**To create a custom native function in Quarkdown, write a Kotlin function that returns `OutputValue<*>` with the `@Name` annotation, export it via `moduleOf` in a `QuarkdownModule`, and register that module in [`Stdlib.kt`](https://github.com/iamgio/quarkdown/blob/main/Stdlib.kt) using `MultiFunctionLibraryLoader`.**

Quarkdown extends Markdown through a **standard library (stdlib)** of native Kotlin functions exposed to the document syntax. Unlike runtime functions created via the `.function` command, native functions are compiled into the library and available globally without parsing overhead. This guide walks through the three-step process to add your own native functions to `iamgio/quarkdown`.

## Step 1 – Define the Native Kotlin Function

Create a Kotlin file under `quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/`. Each native function must return an **`OutputValue<*>`** (or a concrete subclass like `StringValue` or `NumberValue`) and use annotations to control how Quarkdown maps the function name and parameters.

```kotlin
package com.quarkdown.stdlib

import com.quarkdown.core.function.value.NumberValue
import com.quarkdown.core.function.value.OutputValue
import com.quarkdown.core.function.value.wrappedAsValue
import com.quarkdown.core.function.reflect.annotation.Name
import com.quarkdown.core.function.reflect.annotation.LikelyNamed

/**
 * Returns the square of a numeric argument.
 *
 * Quarkdown usage:
 *   .square {5}          → 25
 *   .square number:{7}   → 49
 */
@Name("square")                     // Exposes the function as `.square` in Quarkdown
fun square(
    @LikelyNamed number: NumberValue   // Accepts a numeric value, can be passed positionally or by name
): OutputValue<*> =
    // NumberValue already contains the Kotlin number; we simply multiply it.
    NumberValue(number.value * number.value).wrappedAsValue()

```

**Key implementation details:**
- **`@Name`** specifies the callable name in Quarkdown (e.g., `.square`). Without it, the Kotlin function name is used.
- **`@LikelyNamed`** indicates the parameter is usually passed by name (e.g., `number:{5}`) but also works positionally.
- **`.wrappedAsValue()`** converts the raw Kotlin result back into a Quarkdown-compatible `OutputValue`.

## Step 2 – Export the Function in a QuarkdownModule

Functions must be bundled into a **`QuarkdownModule`** to be discoverable by the stdlib loader. In the same file (or a separate one), create a module using `moduleOf`:

```kotlin
package com.quarkdown.stdlib

import com.quarkdown.core.function.library.module.QuarkdownModule
import com.quarkdown.core.function.library.module.moduleOf

/**
 * Module that contains math-related native functions.
 */
val MyMath: QuarkdownModule = moduleOf(
    ::square          // List every function you want to expose from this file
)

```

The **`moduleOf`** function accepts function references and returns a module object that the stdlib can import.

## Step 3 – Register the Module with the Stdlib Loader

Open **[`quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Stdlib.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Stdlib.kt)**. Import your new module and add it to the `MultiFunctionLibraryLoader` chain:

```kotlin
// Inside quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Stdlib.kt
...
import com.quarkdown.stdlib.MyMath   // ← NEW IMPORT

object Stdlib : LibraryExporter {
    override val library: Library
        get() =
            MultiFunctionLibraryLoader(name = "stdlib")
                .load(
                    Document,
                    Layout,
                    Text,
                    Primitives,
                    MiscElements,
                    Math,
                    Logical,
                    String,
                    Icon,
                    Emoji,
                    Collection,
                    Dictionary,
                    Optionality,
                    Logger,
                    Flow,
                    TableComputation,
                    Data,
                    Localization,
                    Library,
                    Slides,
                    Ecosystem,
                    Html,
                    Mermaid,
                    Reference,
                    Bibliography,
                    MyMath,                     // ← NEW MODULE
                )...

```

After registering, rebuild the project to compile the new function into the stdlib:

```bash
./gradlew installDist

```

Once built, the function is available in any Quarkdown document:

```markdown
.square {6}                <!-- yields 36 -->
.square number:{8}         <!-- yields 64 -->

```

## Understanding the Runtime Alternative

The **Flow module** ([`quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Flow.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Flow.kt), lines 93-104) demonstrates how the `.function` command creates user-defined functions at runtime using **`SimpleFunction`**. This approach wraps expressions dynamically but incurs parsing overhead. Native functions defined in Kotlin bypass this runtime machinery entirely, offering better performance and type safety by leveraging compile-time registration via the stdlib loader.

## Summary

- **Define** the function in `quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/` using Kotlin and the `@Name` annotation, ensuring it returns `OutputValue<*>` or a concrete value type.
- **Export** the function in a `QuarkdownModule` using `moduleOf(::yourFunction)` to make it discoverable.
- **Register** the module in [`Stdlib.kt`](https://github.com/iamgio/quarkdown/blob/main/Stdlib.kt) by adding it to the `MultiFunctionLibraryLoader` list and rebuilding with `./gradlew installDist`.

## Frequently Asked Questions

### How do I change the callable name of a native function in Quarkdown?

Use the **`@Name`** annotation on the Kotlin function. For example, `@Name("pow") fun power(...)` exposes the function as `.pow` in Quarkdown documents while keeping `power` as the internal Kotlin name.

### What return types are valid for native functions?

Native functions must return **`OutputValue<*>`** or a concrete subclass such as `StringValue`, `NumberValue`, or `BooleanValue`. Use `.wrappedAsValue()` to convert raw Kotlin types into Quarkdown values that the interpreter can handle.

### Where is the central stdlib loader defined?

The **`MultiFunctionLibraryLoader`** is instantiated in **[`quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Stdlib.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Stdlib.kt)**. This file aggregates all modules (Document, Layout, Flow, etc.) into the standard library that is shipped with the application.

### Do I need to rebuild the project after adding a native function?

Yes. Unlike runtime functions created with `.function`, native functions are compiled Kotlin code. Run **`./gradlew installDist`** to recompile the stdlib module and make the new function available to the Quarkdown interpreter.