Submitting Lambdas: A Guide
According to meta consensus the default for most challenges allows submitting lambdas/anonymous function bodies. Part of this consensus is that the declaration/assignment of the lambda doesn't have to be included. Take the typical lambda:
val f={a:Int,b:Int->a+b}
We can omit the declaration, that being val f=
which saves us 6 bytes.
val f= // declaration
{a:Int,b:Int->a+b} // lambda
For the rest of this tip I will write the declaration on one line and the lambda body on the next line to make it clear that it is separate. Know that the declaration line won't count to the score; on TIO you'd include it in the Header and put the lambda as the Code (see here.)
Type Inference and You
Kotlin has a fairly robust system of type inference. If the compiler can infer the type of something, you can omit the type information. Lambdas themselves are typed; their type signatures are expressed as:
(Param1Type, ...ParamNType) -> ReturnType
Therefore, the type of the above is (Int, Int) -> Int
.
The type information of a lambda can be inferred from two places:
- The lambda body
- The declaration/use site (when assigned to a variable with a type annotation, or passed to a function)
In the given example above, the types of the parameters are stated in the lambda body, and the return type of the lambda can be inferred by the expression in the body because the types of everything is known. This is why the type of the lambda doesn't have to be stated in the declaration; the compiler knows its complete type signature!
...but what if we did?
A Parlor Trick
val f:(Int,Int)->Int=
{a,b->a+b}
By moving the type information of the lambda into the declaration, it's removed from our actual answer. Thanks to inference, the types of a
and b
are known, and the return type is known, so this compiles, and it's free bytes!
Inferred it
Parameter for Single-Param Lambdas
If a lambda only takes one parameter and its type can be completely inferred, you don't even have to specify a parameter in the lambda body! The inferred parameter will be named it
. Take the two examples:
val shuf:(List<T>)->List<T>=
{l->l.shuffled()}
// -2
val shuf:(List<T>)->List<T>=
{it.shuffled()}
The compiler knows that this lambda takes one parameter and it knows what type it is, so if it isn't bound to a name in the lambda body, it is named it
instead. You can use this to save 2 bytes in the degenerate case above, but if you have to refer to the parameter many times, it will be shorter to simply give it a name as in the former.
{it*it*it*it}
{n->n*n*n*n}
Lambdas With Receiver
Kotlin allows you to specify a receiver parameter for a lambda. Basically, you can rebind this
inside. You indicate this lambda takes a receiver by writing the receiver type and a dot before the type signature:
Recv.(Param1, ...ParamN) -> Ret
Where this is useful is highly dependent on your approach.
val f:(Int,Int)->Int=
{a,b->a+b}
// -1
val f:Int.(Int)->Int=
{this+it}
Of course, what's most useful is that this
is the implicit receiver of member access. So if your submission consists mostly of a call chain on one of the parameters, or you refer to a field a couple times, this is extremely useful.
val shuf:(List<T>)->List<T>=
{l->l.shuffled()}
// -5
val shuf:List<T>.()->List<T>=
{shuffled()}
It can be a bad call to use this
instead of it
e.g.
- If there's only one parameter to the lambda and you don't need to call any methods on it
- You need to destructure the parameter
- etc.
Some Examples
Examples can help you learn!