A `Function` value is a value like any other: It can be passed around like an argument, returned, stored, and so on. The most common way of creating a new `Function` is with `define`.

### Definitions

All `Function` values created through `define` cannot be changed. The simplest definition is a `Function` that does not take any input or return any value.

``````define hello
{
print("Hello!") # Hello!
}

var h: Function() = hello

h()
``````

A `Function` that does not specify a return value actually returns `unit` of type `Unit` behind the scenes. Doing so allows `Function`s that do and don't return values to be treated equally by methods such as `List.each`.

Now for a more useful function.

``````define add(left: Integer, right: Integer): Integer
{
return left + right
}

var a: Function(Integer, Integer => Integer) = add

print(a(1, 2)) # 3
``````

In the first example, parentheses are omitted because no arguments are taken. Similarly, the colon (which comes before the return type) is omitted because no value is explicitly returned.

It is a syntax error to have empty parentheses in any definition, or to have a colon if there is no return type.

Using a function as an argument can be done as follows.

``````define square(input: Integer): Integer
{
return input * input
}

define apply_action(a: Integer, fn: Function(Integer => Integer)): Integer
{
return fn(a)
}

print(apply_action(10, square)) # 100
``````

### Lambdas

In some cases, a `Function` is necessary but creating a permanant definition is unnecessary. In such cases, a lambda can be used.

The return value of a lambda is the last expression inside of it. If a lambda finishes with a statement (such as an if block), it will return `unit` like the `noop` above.

``````var numbers = [2, 4, 6]

numbers = numbers.map((|a| a * a))

print(numbers) # [4, 16, 36]
``````

In the above example, the lambda is the first argument to a function. In such cases, the opening and closing parentheses can be omitted.

``````var numbers = [1, 2, 3]

numbers = numbers.map(|a| a * a)

print(numbers) # [1, 4, 9]
``````

It is possible to create a lambda that does not take arguments.

``````var hello: Function(=> String) = (|| "Hello" )

print(hello()) # Hello
``````

Lambda arguments allow type inference.

``````var math_ops = ["+" => (|a: Integer, b: Integer| a + b),
"-" => (|a, b| a - b)]

print(math_ops["+"](1, 2)) # 3
``````

Lambdas do not support keyword arguments, optional arguments, or variable arguments.

### Closures

The above declarations only use global variables and arguments provided. A closure is a `Function` that uses variables in an upward scope (upvalues).

Any definition (including class methods) can be a closure.

``````define get_counter: Function( => Integer)
{
var counter = 0

define counter_fn: Integer
{
counter += 1
return counter
}

return counter_fn
}

var c = get_counter()
var results = [c(), c(), c()]

print(results) # [1, 2, 3]
``````

Lambdas can also be closures.

``````define list_total(l: Integer...): Integer
{
var total = 0

l.each(|e| total += e )

}

print(list_total(1, 2, 3)) # 6
``````

Class constructors and methods can be closures too. Class methods cannot close over `self`, and neither can close over parameters to a class constructor.

``````enum Status
{
Fail,
Pass,
Skip
}

class Totals(input: Status...)
{
public var @fail_count = 0
public var @pass_count = 0
public var @skip_count = 0

{
var fail = 0
var pass = 0
var skip = 0

input.each(|e|
match e: {
case Fail:
fail += 1
case Pass:
pass += 1
case Skip:
skip += 1
}
)

@fail_count = fail
@pass_count = pass
@skip_count = skip
}
}

var t = Totals(Fail,
Pass, Pass, Pass,
Skip, Skip)

print(t.fail_count) # 1
print(t.pass_count) # 3
print(t.skip_count) # 2
``````

### Features

Functions in Lily have a number of different features available to them. All function kinds except for lambdas can make use of all of these features.

### Forward

Occasionally, two definitions rely on each other. In such cases, `forward` allows creating a definition that will be resolved later.

When there are one or more unresolved forward definitions, both variable declaration and import are blocked.

It is a syntax error to have unresolved definitions at the end of a scope.

``````forward define second(Integer, Integer): Integer { ... }

define first(x: Integer, total: Integer): Integer
{
if x != 0: {
x -= 1
total = second(x, total * 2)
}

}

define second(x: Integer, total: Integer): Integer
{
if x != 0: {
x -= 1
total = first(x, total * 2)
}

}

print(first(4, 2)) # 32
``````

Class methods can be forward declared as well. Similar to forward definitions, class properties cannot be declared while there are unresolved methods.

``````class Example
{
forward private define second(Integer, Integer): Integer { ... }

# Declaring properties here is not allowed.

public define first(x: Integer, total: Integer): Integer
{
if x != 0: {
x -= 1
total = second(x, total * 2)
}

}

private define second(x: Integer, total: Integer): Integer
{
if x != 0: {
x -= 1
total = first(x, total * 2)
}

}
}

print(Example().first(4, 2)) # 32
``````

### Varargs

A definition can be allowed to take an arbitrary number of values, with `...`.

``````define sum(numbers: Integer...): Integer
{
var total = 0
numbers.each(|e| total += e )
}

var s: Function(Integer... => Integer) = sum

print(s())        # 0
print(s(1, 2, 3)) # 6
``````

Class constructors, class methods, enum methods, and variants all allow for variable arguments.

### Optargs

A definition can specify optional values with `*<type>=<value>`. The value given can be simple, or an expression. Required arguments cannot occur after an optional argument.

``````define optarg(a: *Integer = 10): Integer
{
return a + 10
}

var o: Function(*Integer => Integer) = optarg

print(optarg(100)) # 110
print(o())         # 20
``````

Optional arguments are evaluated in the callee's scope.

``````define return_a(a: *String=__function__): String
{
return a
}

var r: Function(*String) = return_a

print(r())       # return_a
print(r("asdf")) # asdf
``````

Additionally, optional arguments are evaluated each time they are seen.

``````define modify(v: Integer,
a: *List[Integer]=[1, 2, 3])
: List[Integer]
{
a.push(v)
return a
}

var m: Function(Integer, *List[Integer] => List[Integer]) = modify

print(m(4))  # [1, 2, 3, 4]
print(m(10)) # [1, 2, 3, 10]
print(m(70)) # [1, 2, 3, 70]
``````

Optional arguments are evaluated from left to right. Arguments on the right can depend on arguments to their left.

``````define my_slice(source: List[Integer],
start: *Integer = 0,
end: *Integer = source.size()): List[Integer]
{
return source.slice(start, end)
}

print(my_slice([1, 2, 3], 1))    # [2, 3]
print(my_slice([4, 5, 6], 0, 1)) # 
``````

Variable and optional arguments can be mixed. By default, the vararg parameter receives an empty `List` if no values are passed. Mixing these two features allows a different default value:

``````define optarg_sum(a: Integer,
b: *Integer = 10,
args: *Integer... = [20, 30]): Integer
{
var total = a + b

for i in 0...args.size() - 1: {
total += args[i]
}

}

var opt_sum: Function(Integer,
*Integer,
*Integer...
=> Integer) = optarg_sum

print(opt_sum(5))              # 65
print(opt_sum(5, 20))          # 75
print(opt_sum(10, 20, 30, 40)) # 100
``````

### Keyargs

A definition can specify keyword arguments by using `:<keyword> <name>: <type>`. Keyword arguments can be used give clarity when multiple arguments of a particular type are passed. Keyword arguments can also be passed in a custom order, unlike position arguments.

``````define simple_keyarg(:first  x: Integer,
:second y: Integer,
:third  z: Integer): List[Integer]
{
return [x, y, z]
}

print(simple_keyarg(1, 2, 3))                         # [1, 2, 3]
print(simple_keyarg(1, :second 2, :third 3))          # [1, 2, 3]
print(simple_keyarg(:third 30, :first 10, :second 5)) # [10, 5, 30]
``````

It isn't necessary to name all arguments:

``````define tail_keyarg(x: Integer, :y y: Integer): Integer
{
return x + y
}

print(tail_keyarg(10, 20))    # 30
print(tail_keyarg(10, :y 20)) # 30
``````

Calling a function with keyword arguments has some restrictions.

``````define simple_keyarg(:first  x: Integer,
:second y: Integer,
:third  z: Integer): List[Integer]
{
return [x, y, z]
}

# SyntaxError: Positional argument after keyword argument.
# simple_keyarg(:first 1, 2, 3)

# SyntaxError: Duplicate value provided to the first argument.
# simple_keyarg(1, :first 1, 2, 3)
``````

Keyword arguments are evaluated and contribute to type inference in the order that they're provided.

``````var keyorder_list: List[Integer] = []

define keyorder_bump(value: Integer): Integer
{
keyorder_list.push(value)
return value
}

define keyorder_check(:first  x: Integer,
:second y: Integer,
:third  z: Integer): List[Integer]
{
return keyorder_list
}

var check = keyorder_check(:second keyorder_bump(2),
:first  keyorder_bump(1),
:third  keyorder_bump(3))

print(check) # [2, 1, 3]
``````

Finally, different argument styles can be mixed.

``````define optkey(:x x: *Integer = 10,
:y y: *Integer = 20): Integer
{
return x + y
}

print(optkey())        # 30
print(optkey(50, 60))  # 110
print(optkey(:y 170))  # 180
print(optkey(4, :y 7)) # 11

define varkey(:format fmt: String,
:arg args: *String...=["a", "b", "c"]): List[String]
{
args.unshift(fmt)
return args
}

print(varkey("fmt"))                # ["fmt", "a", "b", "c"]
print(varkey("fmt", "1", :arg "2")) # ["fmt", "1", "2"]
``````