# Functional Metaprogramming {#functional_metaprogramming}

Functional metaprogramming allows for the dynamic creation and manipulation of function definitions, enabling functions to be defined, modified, or executed during runtime. This approach simplifies the process of defining functions on the fly.

## Obtaining Function Definitions Dynamically {#topic_ilj_zq1_5cc}

In DolphinDB, a function definition is represented by the data type FUNCTIONDEF. You can use the built-in function `funcByName` to dynamically retrieve a function definition based on the its name.

For example, a variable *name* contains function name “sin”. You can use `funcByName` to obtain function definition of sin based on its name stored in *name*. Then you can pass *v*to the function for calculation.

``` {#codeblock_ihl_1r1_5cc}
name = `sin
v = 1..10
funcByName(name)(v)

// output: [0.8415,0.9093,0.1411,-0.75688,-0.9589,-0.2794,0.657,0.9894,0.4121,-0.5440]
```

**Note:** If a function is defined within a module, you can include the module name as a prefix, separated by the namespace symbol `::`.

There are scenarios where you might need to work with anonymous functions \(including lambda expressions\). To handle these cases, you can pass the function definition as a string variable to `parseExpr`. `ParseExpr` returns the metacode, which can be evaluated with `eval`.

For example:

``` {#codeblock_mzc_dr1_5cc}
funcDef = "x->1 + x + x*x"
parseExpr(funcDef).eval().call(2)
//output: 7
```

## Calling Dynamically-Generated Functions {#topic_lzt_dr1_5cc}

DolphinDB offers three methods for invoking dynamic functions: the higher-order function `call`, the operator `()`, and the `at` function.

``` {#codeblock_svq_2r1_5cc}
f = parseExpr("{x,y->(x - y)/(x + y)}").eval()

// method 1: using the call function
call(f, 3.0, 2.0)

// method 2: using operator ()
f(3.0, 2.0)

// method 3: using the at function
at(f, (3.0, 2.0))
```

The first two methods, `call` and `()`, are similar in that they accept a variable number of arguments, requiring that each argument of the target function be individually provided during invocation.

The `at` function, however, always takes exactly two arguments: the function definition and the argument\(s\) needed for the function. This fixed two-argument syntax offers advantages in dynamic invocation scenarios, reducing complexity and potential errors. When calling a function requiring multiple arguments, the second argument of the `at` function must be a tuple, with each element representing one of the function's inputs.

A special case arises when the function being called is a unary function that itself takes a tuple as its single input. In this case, to use the `at` function correctly, you must use the `enlist` function to wrap the input tuple as a new tuple with the original tuple as its sole element. Without this step, the system would interpret the elements of the original tuple as separate arguments, leading to an error.

For example:

``` {#codeblock_zys_fr1_5cc}
f = {x->x.head()\x.tail()}
x = (1,2,3)
// Reports error: The function [] expects 1 argument(s), but the actual number of arguments is: 3
at(f, x)
// correct syntax
at(f, enlist x)
```

## Generating Function Calls Dynamically {#topic_db1_gr1_5cc}

While the dynamic function calls discussed earlier execute immediately and return a value, there are scenarios where we need to create code containing dynamic function calls for later execution, such as defining filtering conditions with functions in SQL queries.

To facilitate this, DolphinDB provides two specialized functions: `makeCall` and `makeUnifiedCall`. These functions generate code for function calls that can be used in lazy execution.

The key difference between them mirrors the distinction between the `call` and `at` functions: `makeCall` requires you to input arguments based on the number needed by the corresponding function. `makeUnifiedCall` requires a fixed number of arguments. If the function requires multiple arguments, they are encapsulated in a tuple.

For example, there is a lambda function that takes two arguments, *x* and *y*. We want to use this function with two columns from a table, “qty1” and “qty2”, as input arguments.

-   Using `makeCall`: Use `sqlCol` to generate metacode for each input column.
-   Using `makeUnifiedCall`: a. Manually create a tuple with each element generated with `sqlCol`, or b. use `sqlTuple` to generate metacode with a tuple expression.

``` {#codeblock_wt5_hr1_5cc}
f = parseExpr("{x,y->(x - y)/(x + y)}").eval()
makeCall(f, sqlCol("qty1"), sqlCol("qty2"))
makeUnifiedCall(f, (sqlCol("qty1"), sqlCol("qty2")))
makeUnifiedCall(f, sqlTuple(`qty1`qty2))
```

Further, the result of `makeCall`/`makeUnifiedCall` can be passed as the parameter *select* of function `sql` to generate SQL dynamically. The selected columns will be processed with `f`.

``` {#codeblock_td4_3r1_5cc}
f = parseExpr("{x,y->(x-y)/(x+y)}").eval()
t = table(1.0 2.0 3.0 as qty1, 1.0 3.0 7.0 as qty2)
sql(select=makeCall(f, sqlCol("qty1"), sqlCol("qty2")), from=t).eval()
sql(select=makeUnifiedCall(f, (sqlCol("qty1"), sqlCol("qty2"))), from=t).eval()
sql(select=makeUnifiedCall(f, sqlTuple(`qty1`qty2)), from=t).eval()
```

Executing line 3, 4, 5 can obtain the same result:

|\_qty1|
|------|
|0|
|-0.2|
|-0.4|

