**Functions** # Function types - A _function_ is an abstraction of behavior. - A function takes in zero or more arguments, and may or may not return a useful value. - Kotlin defines a special type called `Unit`. The `Unit` type has only one value, also called `Unit`. - A function that returns no useful value is assumed by Kotlin to return `Unit`. In this way, one can say that every Kotlin function returns a value. - The _type of a function_ is determined by its list of parameter types, and its return type. - Note that the terms _argument_ and _parameter_ mean almost the same thing. We call something an _argument_ when it appears in a function call, and call it a _parameter_ when it appears in the function definition. # Examples - What distinguishes a value of function type from values of other types is that a function value denotes a behavior, while other types denote regular data. A function can be _invoked_ or _called_ to get that behavior and a return value. - We have called Kotlin functions already. For example, ```kotlin print("Value of a is ") // line 1 print(1) // line 2 println() // line 3 ``` - The `print` function called in Line 1 takes a String argument and returns Unit. - The `print` function called in Line 2 takes an Int argument and returns Unit. - The `println` function called in Line 3 takes no argument and returns Unit. - The `print` function in Line 1 is not the same as the `print` function in Line 2. Kotlin allows two different functions to have the same name but different lists of argument types. Such functions are said to be _overloaded_. # Writing function types - Here is how we write function types in Kotlin: ```kotlin () -> Unit () -> Int (Double) -> String (Int, Double) -> Unit ``` - The parens `()` and arrow `->` symbols are necessary. They cannot be omitted. - `() -> Unit` is the type of a function that takes no argument and returns no useful value. - `() -> Int` is the type of a function that takes no argument and returns an Int value. - `(Double) -> String` is the type of a function that takes a Double argument and returns a String value. - `(Int, Double) -> Unit` is the type of a function that takes two arguments, the first of which is an Int, the second of which is a Double, and returns no useful value. # First-class citizens - Values of the basic types we have studied are called _first-class citizens_ because 1. they can be assigned to a variable, and 1. they can be assigned to an element of an aggregate type like array, and 1. they can be used as a function argument, and 1. they can be returned from a function - **Kotlin functions are also first-class citizens!** # Function literals - A function literal is a source-code-level way to write a function value. - Kotlin has two kinds of function literals: 1. _lambda expressions_ 1. _anonymous functions_ - Just like literals of other types, function literals are meant to be used in a context where the value is used and immediately thrown away, like in an expression. # Syntax of lambda expressions - Syntactically, a lambda expression has a body enclosed in a pair of opening and closing braces. - The body has three parts. It 1. starts with a list of comma-separated parameter declaration `name : type`, 1. is then followed by the arrow `->`, and 1. finally ends in a sequence of one or more expressions. # Examples - This lambda expression denotes a function that takes no argument and returns `Unit`: ```kotlin { -> println("My type is () -> Unit") } ``` It can be abbreviated as ```kotlin { println("My type is () -> Unit") } ``` - This lambda expression denotes a function that takes no argument and returns an Int: ```kotlin { -> 42 } ``` It can be abbreviated as ```kotlin { 42 } ``` - Note that _for functions that take no arguments, the arrow can be omitted_. # Return values of lambda expressions - The return value of a lambda expression is the value of the last expression before the closing brace. - E.g., this lambda expression ```kotlin { print("hi"); 1 } ``` is a function that takes no argument, prints the String "hi" to the screen, and returns 1. Its type is ```kotlin () -> kotlin.Int ``` # Exercise - List the similarities and differences between a block and a lambda expression. # What is _it_? - We have mentioned that when writing a lambda expression representing a function that takes no parameter, we can omit the `->`. - It turns out that functions that take exactly one parameter are used so often that Kotlin provides a _syntactic sugar_ to allow us to sometimes omit the name and type of the sole function parameter, and the `->` as well. In such a case, we use the default name `it` for that parameter. - Here is an example. We want to define a function `twice` that takes a single `Int` parameter and returns an `Int` result whose value is twice that of the argument. We can write `twice` like this ```kotlin val twice: (Int) -> Int = { 2 * it } ``` instead of the longer ```kotlin val twice: (Int) -> Int = { i: Int -> 2 * i } ``` # Anonymous functions - Let's look at this example anonymous function: ```kotlin fun (): Int { print("hi") return 1 } ``` - It denotes exactly the same function represented by the lambda expression `{ print("hi"); 1 }`. - Syntactically, an anonymous function starts with the keyword `fun` followed by a list of comma-separated arguments with type annotations enclosed in a pair of parentheses, followed by a colon and the return type. It finally ends in a block body. - Within its body there must be at least one _return statement_ if the function returns a non-Unit value. - This is an example anonymous function ```kotlin fun (name: String, score: Int): String { println("$name scored $score") return if (score > 95) "exceptional" else if (score > 90) "excellent" else "ok" } ``` - In case the function returns Unit, we can omit the return type annotation `: Unit` and the return statement `return Unit`. For example, instead of writing ```kotlin fun (): Unit { print("Hi there") return Unit } ``` we can write ```kotlin fun () { print("Hi there") } ``` instead. # Defining functions - We can define a function using the same syntax as when we define variables. That is, we write `var` or `val`, then write the function name (and type annotation if needed), then write `=`, then write either a lambda expression or an anonymous function. - E.g. either one of these two ways work fine: ```kotlin val f: () -> Unit = { print("Hi there") } val f = fun (): Unit { print("Hi there") } ``` - With the help of type inference, we can write even more succinctly like this: ```kotlin val f = { print("Hi there") } ``` or this: ```kotlin val f = fun () { print("Hi there") } ``` - Another way to define a function is to take an anonymous function and insert the name of the function to be defined after the keyword `fun`, e.g., ```kotlin fun f(): Unit { print("Hi there") } ``` - Of course, since `f` returns `Unit`, its return type can be omitted to get ```kotlin fun f() { print("Hi there") } ``` - This is the usual way people define functions in Kotlin! - One syntactic sugar is that if the body of the function consists of only one expression, we can remove the braces and use the = sign to define the function, like this: ```kotlin fun double(n: Int): Int = 2 * n ``` - One further simplification is that in case the return type can be inferred from the expression, we can omit it all together. So the above can be written ```kotlin fun double(n: Int) = 2 * n ``` # Calling functions using positional arguments - Functions are called by writing any expression that evaluates to a function value, followed by the comma-delimited arguments, surrounded by a pair of parentheses. E.g., ```kotlin val f: (Int) -> Int = { 2 * it } // The following call to f returns an Int of value 6. f(3) fun g(i: Int, s: String, d: Double) { println("$s $i items is $d") } /* The following function call to g will print the string "The average of 5 items is 1.414", without the quotes, and return nothing useful. */ g(3 + 2, "The average of", 1.414) ``` - This method of calling functions uses _positional arguments_. Arguments are given in the same _number_, _type_ and _order_ as when the function was defined. - The parens are needed even for functions that take no arguments, e.g., ```kotlin println() ``` - One calls `member functions` by using the dot notation, e.g., ```kotlin val s: String = "hello" if (s.startsWith("a")) println("$s starts with 'a'") ``` # Default arguments - Function parameters can be specified to take on default values. These default values are used when the corresponding arguments are omitted at function call. E.g., if a function `f` is defined as ```kotlin fun f(s: String, i: Int = 2, d: Double = 3.14) { ... } ``` - Then `f` can be called by giving it 1, 2 or 3 arguments. E.g., all these 3 function calls ```kotlin f("hello") f("hello", 2) f("hello", 2, 3.14) ``` are equivalent. # Calling functions using named arguments - Functions can also be called by providing the arguments in the "argument = value" syntax. E.g., for the function `f` defined here ```kotlin fun f(s: String, i: Int = 2, d: Double = 3.14) { ... } ``` we can call it in many different ways, e.g., ```kotlin f(i = 3, s = "Alice", d = 1.2) // line 1 f(d = 2.1, s = "Bob") // line 2 f("Charlie", d = 0.3) // line 3 ``` - The call in line 1 shows that we can provide the named arguments in any order. - The call in line 2 shows that we can still use default arguments when using named arguments. - The call in line 3 shows that we can combine positional arguments and named arguments in the same call, as long as we provide all the positional arguments first. # Nested functions - One function `g` can be defined _locally_ within the definition of another function `f`. - In such a case, + `g` is a local function of `f`, i.e., it is visible only within `f` starting from `g`'s point of definition. + `g` can see all definitions of `f` that occur before `g`'s point of definition. - Functions can be nested arbitrarily deeply. # Example nested function - Here is a simple example of nested function. ```kotlin fun main() { val basic = "ho" fun three(s: String) = s.repeat(3) fun ten(s: String) = "$s ".repeat(10) fun greeting() = ten(three(basic)) println(greeting()) } ``` This program prints ``` hohoho hohoho hohoho hohoho hohoho hohoho hohoho hohoho hohoho hohoho ``` # Higher-order functions - A higher-order function is one that 1. takes some other function(s) as argument(s), and/or 2. returns a function value. - Thus, higher-order functions will have types that look something like ```kotlin (Int) -> (Int) -> Int ((Int) -> Int) -> Int (Int, (Int, Double) -> Char) -> String (Int, Int, Double) -> (Char) -> String ((Double) -> Double, (Double) -> Double) -> (Double) -> Double (((Double) -> Double, (Double) -> Double) -> Double) -> Double ``` # Function argument - A good example of a higher-order function that takes a function argument is the array constructors. - We'll look at the constructor for `IntArray` as an example but what we'll learn will be applicable to constructors of all array types. # IntArray constructor - A function that is associated with an object is called a _member function_. - A constructor is a special kind of _member function_. It's used to construct a new object of that type. - Syntactically, a constructor is called with the type name, e.g. the constructor for an `IntArray` is called `IntArray`. The `IntArray` constructor has this signature ```kotlin (size: Int, init: (Int) -> Int = { 0 }) ``` - This is what it all means. + The first parameter of the constructor is called `size` and has type `Int`; it specifies how many Ints the array will contain. + The second parameter is called `init` and has type `(Int) -> Int`, and, unless told otherwise, will initialize all array elements to 0 by default. + The `init` function parameter takes an argument which is the array index, and returns a value to be initialized for that indexed element. # IntArray constructor - Here are some example calls to construct new IntArray's. ```kotlin /* * The init function is not given, so `a1` will be a new array of 3 Ints, * with each element initialized to 0. */ val a1 = IntArray(3) /* * The init function is given, so `a2` will be a new array of 3 Ints, * with elements initialized so that a2[0] = 0, a2[1] = 1, a2[2] = 2. */ val a2 = IntArray(3, { it }) /* * The init function is given, so a3 will be a new array of 3 Ints, * with elements initialized so that a3[0] = 3, a3[1] = 2, a3[2] = 1. */ val a3 = IntArray(3, { 3-it }) ``` # A syntactic sugar - When the last argument of a function call is a lambda expression, Kotlin allows us to write it outside of the parens. E.g., the line ```kotlin val a3 = IntArray(3, { 3-it }) ``` can be written as ```kotlin val a3 = IntArray(3) { 3-it } ``` and this is the preferred way to write such a function call. # Arrays of non-primitive types - What we learn about the constructor for `IntArray` applies to constructors for arrays of other types as well. - E.g., to create an array of five `String`s and initialize every element to an empty string, you should write ```kotlin val stringArray = Array(5) { "" } ``` - There is also a general builder function called `arrayOf` that can construct an array of any type, and at the same time initialize their elements to the values you provide with the call. - E.g., this line ```kotlin val names = arrayOf("Alice", "Bob", "Charlie") ``` will create an array of 3 strings and assign it to the variable `names`. It also initializes `names[0]` with the string "Alice", `names[1]` with "Bob", and `names[2]` with "Charlie". # Two-dimensional arrays - For each integer $n>1$, Kotlin implements an $n$-dimensional array as an array of $(n-1)$-dimensional array. - E.g., a two-dimensional array (what we call matrix in mathematics) of `Int`s is simply an array of `IntArray`, i.e., its type is ```kotlin Array< IntArray > ``` - Here are some example creation of 2d-arrays in Kotlin. ```Kotlin // The 3x3 zero matrix. val zero3 = Array< IntArray >(3) { IntArray(3) } // The 3x3 identity matrix. val id3 = Array< IntArray >(3) { i -> IntArray(3) { j -> if (i == j) 1 else 0 } } ``` # Function returning function value - In mathematics, if you have two functions $f:A\to B$ and $g:B\to C$, you can form the _composite function_ $g\circ f$, where $(g \circ f): A \to C$ such that $(g\circ f)(a) = g(f(a))$ for all $a\in A$. - Here is a special case of function composition in Kotlin: ```kotlin fun o( f: (Double) -> Double, g: (Double) -> Double): (Double)->Double = { g(f(it)) } ``` It's a special case because we are restricting the sets $A$, $B$, and $C$ to be `Double`. - With the definition of `o` above, we can now write ```kotlin fun f(x: Double) = x + 2.0 fun g(x: Double) = x * x println(o(::f, ::g)(1.0)) // This will print 9.0 ``` - The `::` is the _function reference operator_. Written in front of a function name, it means we want to _reference that function_ instead of calling it. _---San Skulrattanakulchai_