# User Defined Functions

When the pre-defined set of functions is not enough, lenses allows users to define their own functions.

To code such a function, first it is required to pull this JVM dependency, and implement the `UserDefinedFunction` interface:

```bash
io.lenses:lenses-sql-udf:4.0.0
```

The resulting artefact should be added to Lenses deployment location, and it will be loaded automatically by Lenses.

## Implementing a UDF <a href="#implementing-a-udf" id="implementing-a-udf"></a>

In order to implement a UDF, the first step is to define how many arguments a function takes.

Lenses currently supports defining functions that take 1,2,3 or a variable number of arguments.

For each variant, lenses provides a corresponding interface:

* Functions taking 1 argument: `UserDefinedFunction1`
* Functions taking 2 argument: `UserDefinedFunction2`
* Functions taking 3 argument: `UserDefinedFunction3`
* Functions taking a variable number of arguments: `UserDefinedFunctionVarArg`

{% hint style="success" %}
Note that, currently, UDFs need to be put in package **io.lenses.sql.udf**, otherwise Lenses will not pick them up.
{% endhint %}

### Package <a href="#package" id="package"></a>

Please make sure that the `UDF` implementation class belongs to one of the packages specified by the `lenses.sql.udf.packages` configuration option.

### Interface <a href="#interface" id="interface"></a>

For this section, only the variable argument number function will be analysed. The remaining variants work in a very similar way with the only difference being the fact that the `typer` function will have a set number of arguments instead or receiving a variable number of arguments.

```java
public interface UserDefinedFunctionVarArg extends UserDefinedFunction {


    String name();
    /**
     * Defines a mapping from input to output types.
     *
     * @param argTypes List of argument data types.
     * @return The resulting data type.
     * @throws UdfException if one or more of the given data types are not supported by this UDF.
     */
    DataType typer(List<DataType> argTypes) throws UdfException;

    /**
     * Evaluates this UDF for the given argument.
     *
     * @param args List of arguments.
     * @return The result of the evaluation.
     * @throws UdfException if the evaluation failed for some reason.
     */
    Value evaluate(List<Value> args) throws UdfException;
}
```

### String name() <a href="#string-name" id="string-name"></a>

When a query specifies a function name:

```sql
SELECT STREAM foo(bar1,bar2,bar3) FROM source;
```

Lenses will first check the list of pre-defined functions for a matching name. If no match is found, Lenses will then proceed to check if a user defined function (UDF/UDAF) exists. It does so by checking if a function exists for which its name (foo in the example above) matches the one returned by the method `name()`

### DataType typer(List argTypes) throws UdfException <a href="#datatype-typerlistdatatype-argtypes-throws-udfexception" id="datatype-typerlistdatatype-argtypes-throws-udfexception"></a>

In order to reduce the need for multiple functions to be defined (one for each accepted argument type), lenses allows function definitions to specify their type based on the types of its arguments.

The typer for a function that transforms its arguments from Strings into Integers could be defined as such:

```java
public interface UserDefinedFunctionVarArg extends UserDefinedFunction {
    DataType typer(List<DataType> argTypes) throws UdfException {
        for (DataType dt : argTypes) {
            if (!dt.isString()) {
                throw new UdfException("Invalid argument. Function foo only accepts String types.");
            }
        }
        return new LTInt();
    }
}
```

### Value evaluate(List args) throws UdfException; <a href="#value-evaluatelistvalue-args-throws-udfexception" id="value-evaluatelistvalue-args-throws-udfexception"></a>

Finally, in order to specify the function’s behavior the `evaluate` method can be used.

The following example will convert all arguments to an integer and then return their sum.

```java
public interface UserDefinedFunctionVarArg extends UserDefinedFunction {
    Value evaluate(List<Value> args) throws UdfException {
        int result = 0;
        for (Value value : args) {
            int v = Integer.parseInt(value.get());
            result += v;
        }
        return result;
    }
}
```

## Special Considerations <a href="#special-considerations" id="special-considerations"></a>

### State <a href="#state" id="state"></a>

User defined functions are meant to stateless. Because of this, no guarantees are made regarding using instance variables and their usage is highly discouraged.

### Nullability <a href="#nullability" id="nullability"></a>

Nullability in Lenses is a type level concern. As such, if a function can return null values, the typing information must reflect that.

When a function can return a `NullValue`, one should define the typer as an `LTOptional<T>`.

### Testing <a href="#testing" id="testing"></a>

Testing is an important part of any development. In order to test your udf we recommend following the example tests published in the [Lenses UDF Example Repository](https://github.com/lensesio/lenses-udf-example)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.lenses.io/latest/devx/6.1/user-guide/sql-reference/functions/user-defined-functions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
