Sparround

Scope functions & extension functions

Scope functions (let, run, with, apply, also) open a temporary scope on an object. They differ along two axes: how the object is referenced (it vs this) and what is returned (the lambda result vs the object itself).

Extension functions "add" a function to an existing class without touching its code: fun String.toMasked(): String. Under the hood they are static utility functions — dispatched statically, not real members, with no access to private fields.

FunctionObject asReturnsTypical use
`let``it`Lambda resultNull checks: `x?.let { ... }`
`run``this`Lambda resultComputing a value from an object
`with``this`Lambda resultMany operations on one object
`apply``this`The object itselfObject configuration (builder style)
`also``it`The object itselfSide effects: logging, validation

Practical rule: apply for configuration, let for null-safe transforms, also for side effects (logging). Avoid nesting scope functions — it becomes unclear which object each it refers to, and calling that out as an antipattern earns interview points.

🛠 Practice task

Take 3 snippets (from your own code or written fresh) and clean them up with scope functions:

  • Object configuration with apply,
  • A null check with ?.let,
  • A logging/analytics call with also.

Then write a String.maskPan() extension and test it on "4169111122223333".

Done when: each change carries a note on why that particular function fits.

📚 Sources and documentation