Can I update a deeply nested immutable object without making it aware of its context? - oop

Let's imagine I have a nested immutable object graph, along these lines (using Kotlin syntax, but hopefully it's clear):
data class House(val bedroom: Bedroom, val bathroom: Bathroom, val kitchen: Kitchen)
class Bedroom
class Bathroom
data class Kitchen(val oven: Oven, val kettle: Kettle)
class Oven
data class Kettle(val on: Boolean)
var house = House(Bedroom(), Bathroom(), Kitchen(Oven(), Kettle(false)))
Now, I want to switch the kettle on. If the objects were mutable I would just write:
data class Kettle(var on: Boolean) {
fun activate() {
this.on = true
}
}
house.kitchen.kettle.activate()
But because they are immutable I have to write:
data class Kettle(val on: Boolean) {
fun activate(house: House): House {
return house.copy(kitchen = kitchen.copy(kettle = kettle.copy(on = true)))
}
}
house = house.kitchen.kettle.activate(house)
(Actually, it's slightly more complicated, but this pseudo-code will do).
I don't like this, not because it's long, per se, but because the Kettle now needs to know not just about its own internal state, but about the full context it exists in.
How can I rewrite this so that each object can be responsible for providing its own mutation logic, without having to be aware of the full object graph? Or am I just trying to marry object-oriented and functional concepts in an impossible way?

This is where functional lenses show their power. For example, using poetix/klenses,
val kettleLens = +House::kitchen + Kitchen::kettle
var house = House(...)
house = kettleLens(house) { copy(on = true) }

One possible approach I thought of (this is the question asker, by the way) was like this:
data class Kettle(val on: Boolean) {
fun activate() {
return Transform(this, Kettle(on = true))
}
}
class Transform<T>(val what: T, val replacement: T) {
fun <U> apply(x: U): U {
if (x is T && x == what) {
return replacement as U
} else {
return x
}
}
}
The idea here is that a transform is a function you can apply to every object in the graph and it will only modify the what you told it to modify.
So you use it like this:
val transform = house.kitchen.kettle.activate()
house = house.transformEverything(transform)
Where every class has an implementation like this:
data class House(val bedroom: Bedroom, val bathroom: Bathroom, val kitchen: Kitchen) {
fun transformEverything(transform: Transform): House {
return transform(this).copy(
bedroom = bedroom.transformEverything(transform),
bathroom = bathroom.transformEverything(transform),
kitchen = kitchen.transformEverything(transform)
)
}
}
Which recursively gives the transform a chance to modify every object it wants to, and it will only apply it to one.
This approach is bad because:
Tons of boiler-plate giving everything its own dumb version of the transformEverything method
It seems weird (and inefficient) to have to call an function on every object in the graph just to change one.
But it does achieve my goal of the Kettle not needing to know anything about its context, and it's pretty straightforward to write the activate function. Thoughts?

Related

How to pass object as argument in Kotlin

I have these two objects A and B I want to use these objects interchangeably in the digit class like shown in this snippet code
internal object A {
internal const val ZERO = "ZERO"
internal const val ONE = "ONE"
}
internal object B {
internal const val ZERO = "ZERO"
internal const val ONE = "UN"
}
class Digit(Lang: String) {
private var X: Any? = null
init {
when (Lang) {
"eng" -> X = A
"fr" -> X = B
}
}
fun spell() {
println(X.ZERO)
}
}
I want inside the Digit class to use both objects, not at the same time, only when I want the English language the Digit class use object A and when I want french language the Digit class use the object B.
I should use reflection? or is there a better design?
The simplest answer is to have your objects all implement the same interface, e.g.:
interface Language {
val ZERO: String
val ONE: String
}
internal object A: Language {
override val ZERO = "ZERO"
override val ONE = "ONE"
}
// …
You can then set a Language reference to any object implementing that interface.
A related (and more concise) approach might be to use an enum:
enum class Language(val ZERO: String, val ONE: String) {
A("ZERO", "ONE"), B("ZERO", "UN")
}
You can then refer to the objects as Language.A &c.
But in practice, none of these approaches scale well. You're likely to end up with a good number of language strings, and probably quite a few languages to support, and those methods will get long-winded. And hard-coding all the strings will make it much more awkward to manage.
So it's more usual to store all the language strings in resource files and load them in at runtime. You could do that manually, e.g. storing the strings in a map — but many platforms and frameworks support standard ways to select the right language, load the strings, and use them. There are many existing questions about this.
data class Translation(val ZERO: String, val ONE: String)
val a = Translation(ZERO = "ZERO", ONE = "ONE")
val b = Translation(ZERO = "ZERO", ONE = "UN")
class Digit(Lang: Translation) {
private val x = Lang
fun spell() {
println(x.ONE)
}
}
Digit(a).spell()
Digit(b).spell()

Kotlin - How to convert a list of objects into a single one after map operation?

I'm trying to wrap my head around map and reduce operations in Kotlin. At least, I guess it's reduce what I'm trying to do.
Let's say that I have a class called Car that takes any number (varargs constructor) of CarPart. Then, I have a list of CarPart which I'll do a map operation and from the result of the operation I need to build one Car using each subelement, something along these lines:
class CarPart(val description: String)
class Car(vararg val carPart: CarPart)
val carParts = listOf(CarPart("Engine"), CarPart("Steering Wheel")))
carParts.map { it.description.toUpperCase() }
.map { CarPart(it) }
.reduce { acc, carPart -> Car(carPart) } <--- I'm struggling here, is reduce what I should be doing
to construct one car from all the subelement?
PS.1: I know that the class design could be better and not take a varargs, this is just an example of a legacy application I'm refactoring and originally that's a Java class taking varargs which I can't change now.
PS.2: The example of mapping to a String and then creating an object out of that String is just for the sake of the example. The actual code grabs an object within the list.
You can simply use a the spread operator (*) over an array:
val mappedCarParts = carParts
.map { it.description.toUpperCase() }
.map { CarPart(it) }
.toTypedArray()
val car = Car(*mappedCarParts)
// Or even:
val car = carParts
.map { it.description.toUpperCase() }
.map { CarPart(it) }
.toTypedArray()
.let{ Car(*it) }
You could just extract the constructor of the Car outside of the creation of the list. I don't see any reason as to why you'd want it inside.
val car = Car(
*carParts
.map { CarPart(it.description.uppercase(Locale.getDefault())) } //keep the .toUpperCase() if you are using an old version of Kotlin
.toTypedArray()
)
We need the spread operator there in order for the vararg to know that we are passing it the elements of the list and not the list itself.

FP patterns for combining data from different sources (preferrably in Kotlin and Arrow)

Disclaimer upfront: Recently, my interest in functional programming has grown and I've been able to apply the most basic approaches (using pure functions as much as my knowledge and working environment permits) in my job. However, I'm still very inexperienced when it comes to more advanced techniques and I thought trying to learn some by asking a question on this site might be the right idea. I've stumbled over similar issues once every while, so I think there should be patterns in FP to deal with this type of problems.
Problem description
It boils down to the following. Suppose there is an API somewhere providing a list of all possible pets.
data class Pet(val name: String, val isFavorite: Boolean = false)
fun fetchAllPetsFromApi(): List<Pet> {
// this would call a real API irl
return listOf(Pet("Dog"), Pet("Cat"), Pet("Parrot"))
}
This API knows nothing about the "favorite" field and it shouldn't. It's not under my control. It's basically just returning a list of pets. Now I want to allow users to mark pets as their favorite. And I store this flag in a local database.
So after fetching all pets from the api, I have to set the favorite flag according to the persisted data.
class FavoriteRepository {
fun petsWithUserFavoriteFlag(allPets: List<Pet>) {
return allPets.map { it.copy(isFavorite = getFavoriteFlagFromDbFor(it) }
}
fun markPetAsFavorite(pet: Pet) {
// persist to db ...
}
fun getFavoriteFlagFromDbFor(pet: Pet): Boolean {...}
}
For some reason, I think this code dealing with the problem of "fetch one part of the information from one data source, then merge it with some information from another" might benefit from the application of FP patterns, but I'm not really sure in which direction to look.
I've read some of the documentation of Arrow (great project btw :)) and am quite a Kotlin enthusiast, so answers utilizing this library would be very appreciated.
Here's something I'd potentially do. Your code has a couple of important flaws that make it unsafe from the functional programming perspective:
It doesn't flag side effects, so compiler is not aware of those and cannot track how they're are used. That means we could call those effects from anywhere without any sort of control. Examples of effects would be the network query or all the operations using the database.
Your operations don't make explicit the fact that they might succeed or fail, so callers are left to try / catch exceptions or the program will blow up. So, there's not a strong requirement to handle both scenarios, which could drive to missing some exceptions and therefore get runtime errors.
Let's try to fix it. Let's start by modeling our domain errors so we have a set of expected errors that our domain understands. Let's also create a mapper so we map all potential exceptions thrown to one of those expected domain errors, so our business logic can react to those accordingly.
sealed class Error {
object Error1 : Error()
object Error2 : Error()
object Error3 : Error()
}
// Stubbed
fun Throwable.toDomainError() = Error.Error1
As you see, we're stubbing the errors and the mapper. You can put time on designing what errors you'll need for your domain on an architecture level and write a proper pure mapper for those. Let's keep going.
Time for flagging our effects to make the compiler aware of those. To do that we use suspend in Kotlin. suspend enforces a calling context at compile time, so you cannot ever call the effect unless you're within a suspended environment or the integration point (a couroutine). We are going to flag as suspend all operations that would be a side effect here: the network request and all db operations.
I'm also taking the freedom to pull out all DB operations to a Database collaborator just for readability.
suspend fun fetchAllPetsFromApi(): List<Pet> = ...
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>) {
... will delegate in the Database ops
}
}
class Database {
// This would flag it as fav on the corresponding table
suspend fun markPetAsFavorite(pet: Pet): Pet = ...
// This would get the flag from the corresponding table
suspend fun getFavoriteFlagFromDbFor(pet: Pet) = ...
}
Our side effects are safe now. They've become description of effects instead, since we cannot ever run them without providing an environment capable of running suspended effects (a coroutine or another suspended function). In functional jargon we'd say our effects are pure now.
Now, let's go for the second issue.
We also said that we were not making explicit the fact that each effect might succeed or fail, so callers might miss potential exceptions thrown and get the program blown up. We can raise that concern over our data by wrapping it with the functional Either<A, B> data type. Let's combine both ideas together:
suspend fun fetchAllPetsFromApi(): Either<Error, List<Pet>> = ...
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> {
... will delegate in the Database ops
}
}
class Database {
// This would flag it as fav on the corresponding table
suspend fun markPetAsFavorite(pet: Pet): Either<Error, Pet> = ...
// This would get the flag from the corresponding table
suspend fun getFavoriteFlagFromDbFor(pet: Pet): Either<Error, Boolean> = ...
}
Now this makes explicit the fact that each one of those computations might succeed or fail, so the caller will be forced to handle both sides and will not forget about handling the potential errors. We're using the types in our benefit here.
Let's add the logics for the effects now:
// Stubbing a list of pets but you'd have your network request within the catch block
suspend fun fetchAllPetsFromApi(): Either<Error, List<Pet>> =
Either.catch { listOf(Pet("Dog"), Pet("Cat")) }.mapLeft { it.toDomainError() }
We can use Either#catch to wrap any suspended effects that might throw. This automatically wraps the result into Either so we can keep computing over it.
More specifically, it wraps the result of the block in Either.Right in case it succeeds, or the exception into Either.Left in case it throws. We also have mapLeft to map potential exceptions thrown (Left side) to one of our strongly typed domain errors. That is why it returns Either<Error, List<Pet>> instead of Either<Throwable, List<Pet>>.
Note that with Either we always model errors on the left side. This is by convention, since Right represents the happy path and we want our successful data there, so we can keep computing over it with map, flatMap, or whatever.
We can apply the same idea for our db methods now:
class Database {
// This would flag it as fav on the corresponding table, I'm stubbing it here for the example.
suspend fun markPetAsFavorite(pet: Pet): Either<Error, Pet> =
Either.catch { pet }.mapLeft { it.toDomainError() }
// This would get the flag from the corresponding table, I'm stubbing it here for the example.
suspend fun getFavoriteFlagFromDbFor(pet: Pet): Either<Error, Boolean> =
Either.catch { true }.mapLeft { it.toDomainError() }
}
We're stubbing the results again, but you can imagine we'd have our actual suspended effects loading from or updating the DB tables inside each Either.catch {} block above.
Finally, we can add some logic to the repo:
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> =
allPets.map { pet ->
db.getFavoriteFlagFromDbFor(pet).map { isFavInDb ->
pet.copy(isFavorite = isFavInDb)
}
}.sequence(Either.applicative()).fix().map { it.toList() }
}
Ok this one might be a bit more complex due to how our effects are written, but I'll try to make it clear.
We need to map the list so for each pet loaded from network we can load its fav state from the Database. Then we copy it as you were doing. But given getFavoriteFlagFromDbFor(pet) returns Either<Error, Booelan> now we'd have a List<Either<Error, Pet>> as a result 🤔 That might make it hard to work with the complete list of pets, since we'd need to iterate and for each one first we'd need to check whether it's Left or Right.
To make it easier to consume the List<Pet> as a whole, we might want to swap the types here, so we'd have Either<Error, List<Pet>> instead.
To this magic, one option would be sequence. sequence requires the Either applicative in this case since that'll be used to lift the intermediate results and the final list into Either.
We're also using the chance to map the ListK into the stdlib List instead, since ListK is what sequence uses internally, but we can understand it as a functional wrapped over List in broad words, so you have an idea. Since here we're only interested on the actual list to match our types, we can map the Right<ListK<Pet>> to Right<List<Pet>>.
Finally, we can go ahead and consume this suspended program:
suspend fun main() {
val repo = FavoriteRepository()
val hydratedPets = fetchAllPetsFromApi().flatMap { pets -> repo.petsWithUserFavoriteFlag(pets) }
hydratedPets.fold(
ifLeft = { error -> println(error) },
ifRight = { pets -> println(pets) }
)
}
We're going for flatMap since we have sequential ops here.
There are potential optimizations we could do like using parTraverse to load all the fav states from DB for a list of pets in parallel and gather results in the end, but I didn't use it since I'm not sure your database is prepared for concurrent access.
Here's how you could do it:
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> =
allPets.parTraverse { pet ->
db.getFavoriteFlagFromDbFor(pet).map { isFavInDb ->
pet.copy(isFavorite = isFavInDb)
}
}.sequence(Either.applicative()).fix().map { it.toList() }
I think we could also simplify the whole thing a bit more by changing some of the types and how operations are structured but wasn't sure about refactoring it too much from your codebase since I'm not aware of your current team constraints.
And here's the complete codebase:
import arrow.core.Either
import arrow.core.extensions.either.applicative.applicative
import arrow.core.extensions.list.traverse.sequence
import arrow.core.extensions.listk.foldable.toList
import arrow.core.fix
import arrow.core.flatMap
data class Pet(val name: String, val isFavorite: Boolean = false)
// Our sealed hierarchy of potential errors our domain understands
sealed class Error {
object Error1 : Error()
object Error2 : Error()
object Error3 : Error()
}
// Stubbed, would be a mapper from throwable to any of the expected domain errors used via mapLeft.
fun Throwable.toDomainError() = Error.Error1
// This would call a real API irl, stubbed here for the example.
suspend fun fetchAllPetsFromApi(): Either<Error, List<Pet>> =
Either.catch { listOf(Pet("Dog"), Pet("Cat")) }.mapLeft { it.toDomainError() }
class FavoriteRepository(private val db: Database = Database()) {
suspend fun petsWithUserFavoriteFlag(allPets: List<Pet>): Either<Error, List<Pet>> =
allPets.map { pet ->
db.getFavoriteFlagFromDbFor(pet).map { isFavInDb ->
pet.copy(isFavorite = isFavInDb)
}
}.sequence(Either.applicative()).fix().map { it.toList() }
}
class Database {
// This would flag it as fav on the corresponding table, I'm stubbing it here for the example.
suspend fun markPetAsFavorite(pet: Pet): Either<Error, Pet> =
Either.catch { pet }.mapLeft { it.toDomainError() }
// This would get the flag from the corresponding table, I'm stubbing it here for the example.
suspend fun getFavoriteFlagFromDbFor(pet: Pet): Either<Error, Boolean> =
Either.catch { true }.mapLeft { it.toDomainError() }
}
suspend fun main() {
val repo = FavoriteRepository()
val hydratedPets = fetchAllPetsFromApi().flatMap { pets -> repo.petsWithUserFavoriteFlag(pets) }
hydratedPets.fold(
ifLeft = { error -> println(error) },
ifRight = { pets -> println(pets) }
)
}

Kotlin: Mutual assignments

I want to set up two values that hold immutable references to each other. Example:
data class Person(val other: Person)
val jack = Person(jill), jill = Person(jack) // doesn't compile
Note: lateinit doesn't seem to work with data class primary constructors.
Any ideas?
You could get away with something like this:
class Person() {
private var _other: Person? = null
private constructor(_other: Person? = null) : this() {
this._other = _other
}
val other: Person
get() {
if (_other == null) {
_other = Person(this)
}
return _other ?: throw AssertionError("Set to null by another thread")
}
}
And then you would be able to do:
val jack = Person()
val jill = jack.other
Using a data class here does not work for multiple reasons:
First because a data class can't have an empty constructor.
Even if that wasn't a problem, the generated methods would end up having a cyclic dependency and will fail in runtime with java.lang.StackOverflowError. So you'd have to overwrite toString, equals, etc. which kind of defeats the purpose of using data class in the first place.
Here is the trick (note, this is really a trick, you need a good reason to use it in real code).
Unfortunately it won't work with data classes, as they seem to be secured against this kind of hacks.
But if you have java-stile classes, you may use two things to your advantage:
You can initialize vals in the constructor (same as with final in java)
You have access to this inside the constructor (and you may leak it outside if you really want)
Which means that you can create another Person inside the constructor of the first person and finalize the creation of both classes before the constructor finishes.
Once again: exposing this as I did below is a bad idea. When otherFactory is called, it's parameter is only half-initialized. This may lead to nasty bugs, especially if you try to publish such reference in multithreaded environment.
A bit safer approach is to create both Persons inside the constructor of the first Person (you'll need to supply the fields of both entities as arguments). It's safer because you're in control of the code that uses half-initialized this reference.
class Person {
val name: String
val other: Person
constructor(name: String, other: Person) {
this.name = name
this.other = other
}
// !! not very safe !!
constructor(name: String, otherFactory: (Person) -> Person) {
this.other = otherFactory(this)
this.name = name
}
// a bit safer
constructor(name: String, otherName: String) {
this.other = Person(otherName, this)
this.name = name
}
}
val person1 = Person("first") {
Person("second", it)
}
val person2 = person1.other
print(person1.name) // first
print(person2.name) // second
val person3 = Person("third", "fourth")
val person4 = person3.other
print(person3.name)
print(person4.name)
Thanks for your suggestions everybody. I came up with an alternative and would like to hear your insights:
open class Person {
open val other: Person by lazy { Person2(this) }
class Person2(override val other: Person): Person()
}
val jack = Person()
val jill = jack.other
Here we have one person lazily instantiating the other on demand, using an internal subclass that implements other differently (i.e. it is just given it directly in its constructor).
Thoughts most welcome.

Example of when should we use run, let, apply, also and with on Kotlin

I wish to have a good example for each function run, let, apply, also, with
I have read this article but still lack of an example
All these functions are used for switching the scope of the current function / the variable. They are used to keep things that belong together in one place (mostly initializations).
Here are some examples:
run - returns anything you want and re-scopes the variable it's used on to this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
The password generator is now rescoped as this and we can therefore set seed, hash and hashRepetitions without using a variable.
generate() will return an instance of Password.
apply is similar, but it will return this:
val generator = PasswordGenerator().apply {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
}
val pasword = generator.generate()
That's particularly useful as a replacement for the Builder pattern, and if you want to re-use certain configurations.
let - mostly used to avoid null checks, but can also be used as a replacement for run. The difference is, that this will still be the same as before and you access the re-scoped variable using it:
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
The code above will add the apple to the basket only if it's not null. Also notice that it is now not optional anymore so you won't run into a NullPointerException here (aka. you don't need to use ?. to access its attributes)
also - use it when you want to use apply, but don't want to shadow this
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ...
}
Using apply here would shadow this, so that this.weight would refer to the apple, and not to the fruit basket.
Note: I shamelessly took the examples from my blog
There are a few more articles like here, and here that are worth to take a look.
I think it is down to when you need a shorter, more concise within a few lines, and to avoid branching or conditional statement checking (such as if not null, then do this).
I love this simple chart, so I linked it here. You can see it from this as written by Sebastiano Gottardo.
Please also look at the chart accompanying my explanation below.
Concept
I think it as a role playing way inside your code block when you call those functions + whether you want yourself back (to chain call functions, or set to result variable, etc).
Above is what I think.
Concept Example
Let's see examples for all of them here
1.) myComputer.apply { } means you want to act as a main actor (you want to think that you're computer), and you want yourself back (computer) so you can do
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
Yup, you yourself just install the apps, crash yourself, and saved yourself as reference to allow others to see and do something with it.
2.) myComputer.also {} means you're completely sure you aren't computer, you're outsider that wants to do something with it, and also wants it computer as a returned result.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.) with(myComputer) { } means you're main actor (computer), and you don't want yourself as a result back.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.) myComputer.run { } means you're main actor (computer), and you don't want yourself as a result back.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
but it's different from with { } in a very subtle sense that you can chain call run { } like the following
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
This is due to run {} is extension function, but with { } is not. So you call run { } and this inside the code block will be reflected to the caller type of object. You can see this for an excellent explanation for the difference between run {} and with {}.
5.) myComputer.let { } means you're outsider that looks at the computer, and want to do something about it without any care for computer instance to be returned back to you again.
myComputer.let {
myGrandpa.installVirusOn(it)
}
The Way to Look At It
I tend to look at also and let as something which is external, outside. Whenever you say these two words, it's like you try to act up on something. let install virus on this computer, and also crash it. So this nails down the part of whether you're an actor or not.
For the result part, it's clearly there. also expresses that it's also another thing, so you still retain the availability of object itself. Thus it returns it as a result.
Everything else associates with this. Additionally run/with clearly doesn't interest in return object-self back. Now you can differentiate all of them.
I think sometimes when we step away from 100% programming/logic-based of examples, then we are in better position to conceptualize things. But that depends right :)
There are 6 different scoping functions:
T.run
T.let
T.apply
T.also
with
run
I prepared a visual note as the below to show the differences :
data class Citizen(var name: String, var age: Int, var residence: String)
Decision depends on your needs. The use cases of different functions overlap, so that you can choose the functions based on the specific conventions used in your project or team.
Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease your code readability and lead to errors. Avoid nesting scope functions and be careful when chaining them: it's easy to get confused about the current context object and the value of this or it.
Here is another diagram for deciding which one to use from https://medium.com/#elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84
Some conventions are as the following :
Use also for additional actions that don't alter the object, such as logging or printing debug information.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
The common case for apply is the object configuration.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
If you need shadowing, use run
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
If you need to return receiver object itself, use apply or also
let, also, apply, takeIf, takeUnless are extension functions in Kotlin.
To understand these function you have to understand Extension functions and Lambda functions in Kotlin.
Extension Function:
By the use of extension function, we can create a function for a class without inheriting a class.
Kotlin, similar to C# and Gosu, provides the ability to extend a class
with new functionality without having to inherit from the class or use
any type of design pattern such as Decorator. This is done via special
declarations called extensions. Kotlin supports extension functions
and extension properties.
So, to find if only numbers in the String, you can create a method like below without inheriting String class.
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
you can use the above extension function like this,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
which is prints true.
Lambda Functions:
Lambda functions are just like Interface in Java. But in Kotlin, lambda functions can be passed as a parameter in functions.
Example:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
You can see, the block is a lambda function and it is passed as a parameter. You can use the above function like this,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
The above function will print like this,
Block executed
true
I hope, now you got an idea about Extension functions and Lambda functions. Now we can go to Extension functions one by one.
let
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
Two Types T and R used in the above function.
T.let
T could be any object like String class. so you can invoke this function with any objects.
block: (T) -> R
In parameter of let, you can see the above lambda function. Also, the invoking object is passed as a parameter of the function. So you can use the invoking class object inside the function. then it returns the R (another object).
Example:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
In above example let takes String as a parameter of its lambda function and it returns Pair in return.
In the same way, other extension function works.
also
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
extension function also takes the invoking class as a lambda function parameter and returns nothing.
Example:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
apply
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Same as also but the same invoking object passed as the function so you can use the functions and other properties without calling it or parameter name.
Example:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
You can see in the above example the functions of String class directly invoked inside the lambda funtion.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Example:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
In above example number will have a string of phoneNumber only it matches the regex. Otherwise, it will be null.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
It is the reverse of takeIf.
Example:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
number will have a string of phoneNumber only if not matches the regex. Otherwise, it will be null.
You can view similar answers which is usefull here difference between kotlin also, apply, let, use, takeIf and takeUnless in Kotlin
According to my experience, since such functions are inline syntactic sugar with no performance difference, you should always choose the one that requires writing the least amount of code in the lamda.
To do this, first determine whether you want the lambda to return its result (choose run/let) or the object itself (choose apply/also); then in most cases when the lambda is a single expression, choose the ones with the same block function type as that expression, because when it's a receiver expression, this can be omitted, when it's a parameter expression, it is shorter than this:
val a: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
a.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
a.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
a.run/*apply*/ { parameterFunction(this, ...) } // longer
a.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
However, when the lambda consists of a mix of them, it's up to you then to choose the one that fits better into the context or you feel more comfortable with.
Also, use the ones with parameter block function when deconstruction is needed:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
Here is a brief comparison among all these functions from JetBrains's official Kotlin course on Coursera Kotlin for Java Developers:
I must admit that the difference is not so obvious at first glance, among other things because these 5 functions are often interchangeable. Here is my understanding :
APPLY -> Initialize an object with theses properties and wait for the object
val paint = Paint().apply {
this.style = Paint.Style.FILL
this.color = Color.WHITE
}
LET -> Isolate a piece of code and wait for the result
val result = let {
val b = 3
val c = 2
b + c
}
or
val a = 1
val result = a.let {
val b = 3
val c = 2
it + b + c
}
or
val paint: Paint? = Paint()
paint?.let {
// here, paint is always NOT NULL
// paint is "Paint", not "Paint?"
}
ALSO -> Execute 2 operations at the same time and wait for the result
var a = 1
var b = 3
a = b.also { b = a }
WITH -> Do something with this variable/object and don't wait for a result (chaining NOT allowed )
with(canvas) {
this.draw(x)
this.draw(y)
}
RUN -> Do something with this variable/object and don't wait for a result (chaining allowed)
canvas.run {
this.draw(x)
this.draw(y)
}
or
canvas.run {this.draw(x)}.run {this.draw(x)}