Kotlin with copilot: Use lambda function to print the full name
Video: Kotlin with copilot: Use lambda function to print the full name by Taught by Celeste AI - AI Coding Coach
Kotlin: Lambda to Print a Full Name
val printFullName: (String, String) -> Unit = { f, l -> println("$f $l") }. Two-argument lambda that returns nothing — the canonical "side-effect" function.
A small puzzle: store a two-argument print operation as a lambda, then call it. Demos the (args) -> Unit function type for "do something, return nothing."
The Copilot prompt
fun main() {
// Lambda that takes first name and last name and prints the full name
val printFullName: (String, String) -> Unit =
Copilot generates:
val printFullName: (String, String) -> Unit = { firstName, lastName ->
println("Full name: $firstName $lastName")
}
printFullName("John", "Doe") // Full name: John Doe
Walkthrough
1. The type annotation. (String, String) -> Unit — takes two Strings, returns nothing.
Unit is Kotlin's "no value" type — equivalent to void in Java/C. A function returning Unit doesn't have to write return Unit; it's implicit.
2. The lambda. { firstName, lastName -> println(...) }. Two parameters separated by commas. The arrow -> separates parameters from body.
3. The call. printFullName("John", "Doe") — same syntax as calling any function.
Why use a lambda at all?
For one-off logic, a regular function is clearer:
fun printFullName(firstName: String, lastName: String) {
println("Full name: $firstName $lastName")
}
Use a lambda when you need to pass the function around — store it in a variable, pass it as an argument, return it from another function.
Lambdas are first-class values
val printers: Map<String, (String, String) -> Unit> = mapOf(
"casual" to { f, l -> println("Hey $f $l!") },
"formal" to { f, l -> println("Mr/Ms $l, born $f") },
"abbreviated" to { f, l -> println("${f.first()}.$l") },
)
printers["casual"]?.invoke("Alice", "Smith")
// "Hey Alice Smith!"
printers["formal"]?.invoke("Alice", "Smith")
// "Mr/Ms Smith, born Alice"
A Map<String, function> lets you select behaviour by key. Useful for dispatch tables — register handlers without a giant when block.
?.invoke(args) is needed when the value is nullable (Map returns null for missing keys). Without nullable: printers["casual"]("Alice", "Smith") works directly.
Lambdas vs function references
fun printFullName(firstName: String, lastName: String) {
println("Full name: $firstName $lastName")
}
val ref: (String, String) -> Unit = ::printFullName
ref("John", "Doe")
::printFullName produces a function reference — points to the named function. Equivalent to wrapping in { f, l -> printFullName(f, l) } but cheaper (no extra lambda allocation).
When the lambda body is just calling another function, the reference is cleaner.
Type inference
Kotlin can often infer the lambda type without annotation:
val printFullName = { firstName: String, lastName: String ->
println("Full name: $firstName $lastName")
}
If the parameter types are explicit, the lambda's type is inferred. If they're not, you need the type annotation on the variable:
val printFullName: (String, String) -> Unit = { f, l ->
println("Full name: $f $l") // f and l inferred from the type annotation
}
(...) -> Unit vs () -> Unit
val noArgs: () -> Unit = { println("Hi") }
val twoArgs: (String, String) -> Unit = { f, l -> println("$f $l") }
() is the "no parameters" parameter list. (String, String) is two String parameters. The arrow + return type tells you what the function gives back.
Returning Unit explicitly
val explicit: (String, String) -> Unit = { f, l ->
println(f)
println(l)
Unit
}
The trailing Unit is redundant — the last expression's value isn't returned for Unit-typed lambdas. Most code omits it.
Side effects and pure functions
(String, String) -> Unit is the signature of a side-effect function — it returns nothing of value, so its only purpose is to do something (print, write a file, call an API).
Compare to a pure function: (String, String) -> String returns a String, no side effects:
val makeFullName: (String, String) -> String = { f, l -> "$f $l" }
val name = makeFullName("Alice", "Smith") // "Alice Smith"
println(name) // separate side-effect step
Splitting "compute" from "print" makes the compute step testable. The Kotlin stdlib's transformation functions (map, filter, reduce) are pure for this reason.
Common mistakes
Calling instead of storing. val x = printFullName("a", "b") calls the function and stores the return (Unit). To store the function: val x = ::printFullName or val x: (String, String) -> Unit = { ... }.
Wrong number of params. (String, String) -> Unit declared, called with one string → compile error. Match the type.
Trying to return a value from a Unit lambda. The body's last expression is discarded. To return a value, change the return type.
Forgetting commas in multi-arg lambda. { firstName lastName -> ... } is a syntax error. Need the comma.
Confusing (String) -> Unit with (Unit) -> String. Reversed signature — input is Unit (no useful info), output is String. Different shape, different use case.
What's next
Episode 27: Lambda to print a single name. Same idea as today, with one parameter. Smaller building block.
Recap
val f: (P1, P2) -> R = { p1, p2 -> body } for storing functions. (...) -> Unit for side-effect functions. Unit is Kotlin's "no value" return type. Function references via ::name for cheaper call-by-reference. Pure functions (-> String) compose better than impure (-> Unit). Lambdas in maps for dispatch tables.
Next episode: single-arg name lambda.