Kotlin dsl for Mutablelist add - kotlin

here is an extensible function I want to use in adding more element to a mutableList.
fun MutableList<Field>.withFields(block: () -> Field): MutableList<Field> {
this.add(block())
return this
}
here is how I get to use the function
fun dummy(): MutableList<Field> {
return mutableListOf<Field>().withFields {
Field("first")
Field("second")
Field("last")
}
}
only the last item is added to the list and returned.
How do i achieve this?

You have defined your withFields() extension function as taking a function that returns a single Field object. The method calls this function, and then adds the object it returns to the underlying list.
Then you call withFields(), supplying a lambda function. The definition of the return value of a lambda function is the value of the final statement in the function in the case of a block with multiple statements. So your lambda function returns just Field("last"), since that is the result of the last statement in the function. The other Field objects you create are ignored. So that's why just the final value is added to the target list.
To be able to add multiple items at once via some function, you need to define such a function that returns multiple items to be added...like maybe by returning a Iterator, or a List.
UPDATE: So maybe something like this (didn't try to run this):
fun MutableList<Field>.withFields(block: () -> List<Field>): MutableList<Field> {
for (field in block()) {
this.add(field)
}
return this
}
fun dummy(): MutableList<Field> {
return mutableListOf<Field>().withFields {
listOf( Field("first"),
Field("second"),
Field("last"))
}
}

Related

In kotlin what is this syntax: "fun ClassName.funcName(): (Type ) -> Type = {fun body}"

Edited: What and how it work? This is a kotlin dsl language.
fun ClassName.funcName(): (Type ) -> Type = {func body}
um this is one of the implementation I found, and also see the concrete syntax to this link: https://dzone.com/articles/the-complete-custom-gradle-plugin-building-tutoria
private fun CodeLinesExtension.buildFileFilter():
(File) ->
Boolean =
if (fileExtensions.isEmpty()) {
{ true }
} else {
{ fileExtensions.contains(it.extension) } // filter by extension
}
if you CALL this one as a arguments in ".filter(...)", what would happed in the "(File)" syntax, is it automatically receive an arguments?
someFiles.filter(CodeLinesExtension.buildFileFilter()).forEach{...}
There are many things at play here.
fun SomeClass.funcName() is the syntax for declaring extension functions. These are used just like regular methods of the class SomeClass, but can be defined outside of the class, and it's especially useful on classes that you don't control.
The return type of the function you mention is (File) -> Boolean. This expression describes a function type. In this specific case, it is the type of all functions that take a File as single argument, and return a Boolean.
This means that your buildFileFilter() function is an extension function that itself returns a function (File) -> Boolean.
As you can see in the function's body, inside the if statement, there are braces to define the blocks of the if-else ({ ... }), but also braces inside those blocks. This is because Kotlin uses braces for lambda expressions (literals for anonymous functions).
{ true } is a lambda expression that represents a function that may or may not take arguments, but returns true every time.
So this code:
if (fileExtensions.isEmpty()) {
{ true }
} else {
{ fileExtensions.contains(it.extension) } // filter by extension
}
returns either:
{ true } - a function that takes a File as argument and always returns true, or
{ fileExtensions.contains(it.extension) } - a function that takes a File as argument and returns whether fileExtensions contains the extension of the File that it receives (it is the implicit name for the File argument)
Note: the fact that these functions take a File as argument is inferred by the compiler based on the return type of buildFileFilter().
if you CALL this one as a arguments in ".filter(...)", what would happed in the "(File)" syntax, is it automatically receive an arguments?
filter() itself actually expects a function as argument. The function you pass to filter() is called a predicate, because it takes one element of the list as argument and returns a Boolean that says whether this element should be included in the resulting list.
So if you apply filter to a List<File>, the predicate expected by filter is exactly of type (File) -> Boolean (it says whether we should put this File item in the resulting collection).
Calling buildFileFilter() returns a function as we have seen, and the function is perfectly of the right type to use it in filter. The File argument of the function returned by buildFileFilter() will be given by the filter function itself when it calls your filter function.

How to use a predefined lambda in kotlin?

I am learning Kotlin coming from Java, and I stumbled upon an unexpected behavior.
I noticed, that in my below code, I seem to accidentally declare a new lambda at a bad position instead of using the one I already have. How can I fix this?
I wrote these two declarations:
/**
* Dataclass used as an example.
*/
data class Meeple(var name: String, var color: String = "translucent")
/**
* Function to map from a List<T> to a new List of equal length,
* containing the ordered elements received by applying a Mapper's map
* function to every element of the input List.
*
* #param T Type of input List-elements
* #param O Type of output List-elements
* #param mapper The mapping function applied to every input element.
* #return The List of output elements received by applying the mapper on all
* input elements.
*/
fun <T, O> List<T>.map(mapper: (T) -> O?): List<O?> {
val target = ArrayList<O?>();
for (t in this) {
val mapped: O? = mapper.invoke(t)
target.add(mapped);
}
return target;
}
The data class is just a dummy example of a simple object. The List.map extension function is meant to map from the elements of the list to a new type and return a new List of that new type, almost like a Stream.map would in Java.
I then create some dummy Meeples and try to map them to their respective names:
fun main(args: Array<String>) {
val meeples = listOf(
Meeple("Jim", "#fff"),
Meeple("Cassidy"),
Meeple("David", "#f00")
)
var toFilter: String = "Cassidy"
val lambda: (Meeple) -> String? =
{ if (it.name == toFilter) null else it.name }
toFilter = "Jim"
for (name in meeples.map { lambda }) {
println(name ?: "[anonymous]") // This outputs "(Meeple) -> kotlin.String?" (x3 because of the loop)
}
}
I did this to check the behavior of the lambda, and whether it would later filter "Jim" or "Cassidy", my expectation being the later, as that was the state of toFilter at lambda initialization.
However I got an entirely different result. The invoke method, though described by IntelliJ as being (T) -> O? seems to yield the name of the lambda instead of the name of the Meeple.
It seems, that the call to meeples.map { lambda } does not bind the lambda as I expected, but creates a new lambda, that returns lambda and probably internally calls toString on that as well.
How would I actually invoke the real lambda method, instead of declaring a new one?
You already mentioned in the comments you figured out that you were passing a new lambda that returns your original lambda.
As for the toFilter value changing: The lambda function is like any other interface. As you have defined it, it captures the toFilter variable, so it will always use the current value of it when the lambda is executed. If you want to avoid capturing the variable, copy its current value into the lambda when you define the lambda. There are various ways to do this. One way is to copy it to a local variable first.
var toFilter: String = "Cassidy"
val constantToFilter = toFilter
val lambda: (Meeple) -> String? =
{ if (it.name == constantToFilter) null else it.name }
toFilter = "Jim"
Pretty much anything you can do with Stream in Java, you can do to an Iterable directly in Kotlin. The map function is already available, as mentioned in the comments.
Edit: Since you mentioned Java behavior in the comments.
Java can capture member variables, but local variables have to be marked final for the compiler to allow you to pass them to a lambda or interface. So in this sense they capture values only (unless you pass member variable). The equivalent to Java's final for a local variable in Kotlin is val.
Kotlin is more lenient than Java in this situation, and also allows you to pass a non-final local variable (var) to an interface or lambda, and it captures the variable in this case. This is what your original code is doing.
Even though you have found the issue as you mention in comments, I am adding this answer with some details to help any future readers.
So when you create lambda using
val lambda: (Meeple) -> String? = { if (it.name == toFilter) null else it.name }
This basically translates to
final Function1 lambda = (Function1)(new Function1() {
public Object invoke(Object var1) {
return this.invoke((Meeple)var1);
}
#Nullable
public final String invoke(#NotNull Meeple it) {
Intrinsics.checkNotNullParameter(it, "it");
return Intrinsics.areEqual(it.getName(), (String)toFilter.element) ? null : it.getName();
}
});
Now correct way to pass this to your map method would be as you have mentioned in comments
name in meeples.map(lambda)
but instead of (lambda) you wrote { lambda }, this is the trailing lambda convention
name in meeples.map { lambda }
// if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses:
// If the lambda is the only argument in that call, the parentheses can be omitted entirely
this creates a new lambda which returns the lambda we defined above, this line basically gets translated to following
HomeFragmentKt.map(meeples, (Function1)(new Function1() {
public Object invoke(Object var1) {
return this.invoke((Meeple)var1);
}
#Nullable
public final Function1 invoke(#NotNull Meeple it) {
Intrinsics.checkNotNullParameter(it, "it");
return lambda; // It simply returns the lambda you defined, and the code to filter never gets invoked
}
}))

how to apply two types to orElse() in Optional

I want to use orElse() function in Optional, but the first half "engineRepository.findById(engineId)" return Model, and second half return list of Model, how can I make the result return List?
I tried to add listof() to the first half, but I can't use orElse() anymore, how to do that?
var ModelList: List<Model> = try {
EngineRepository.findById(engineId).orElse(EngineRepository.findAllByEnvironment(environment)))
}
#Repository
interface EngineRepository : MongoRepository<Model, String> {
fun findAllByEnvironment(environment: String):List<Model>
}
You should indeed use listOf, but instead of passing the entire expression of EngineRepository.findById(engineId) to it, you should apply it to the Model wrapped inside the Optional returned by findById. To do that, use map:
EngineRepository.findById(engineId)
.map(::listOf)
.orElse(EngineRepository.findAllByEnvironment(environment))
Note that this will execute findAllByEnvironment whether or not findById returned an Optional.empty(). If you don't want that, you should use orElseGet:
.orElseGet { EngineRepository.findAllByEnvironment(environment) }

Can't understand specific kind of kotlin functions

For example:
private fun TextView.onEndDrawableClicked(onClicked: (view: TextView) -> Unit) {
this.setOnTouchListener { v, event ->
var hasConsumed = false
if (v is TextView) {
if (event.x >= v.width - v.totalPaddingRight) {
if (event.action == MotionEvent.ACTION_UP) {
onClicked(this)
}
hasConsumed = true
}
}
hasConsumed
}
}
In the example above we see extension function. I know what it is and can use use/create similar ones. But there's onClicked: (view: TextView) -> Unit in the example's parameters. What is this? Callback? I see this kind of parameters too often, but don't have any idea how to understand that. Does it reference to some lambda function? Can someone send me detailed manual/example of this kind of parameters/functions/whatever?
onClicked is a function that gets passed to onEndDrawableClicked. It's type is a functional type (view: TextView) -> Unit. That function, when called, expects one parameter view of type TextView and returns Unit.
So yes, it is a callback, passed as a lambda. It can be used like:
val textView: TextView = ...
textView.onEndDrawableClicked {
// code that should be executed when onClicked gets called.
}
Functions are first class citiziens in Kotlin. Meaning you can store function in variables and pass them to other functions. Or let functions return functions.
When to use?
You can pass a function whenever you want to pass behavior (rather than state) to or from your functions. So the caller can decide what to do in those cases. That allows you to write highly flexible code / API's.
Alternatively you could create an interface with one ore many functions to be called and pass an instance of that interface to your function.

Kotlin fun() vs lambda is there difference?

This question is about fun() vs a lambda block definitions and scopes.
i have tried define the expressions in two ways. Here is what i have tried:
val myFunction = fun(){
println("i am in a function")
}
//but i also tried doing this:
val myFunction = {
println("i am in a lambda")
}
my problem is i do not know if they are equivalent and same thing ?
The differences are best described in https://kotlinlang.org/docs/reference/lambdas.html#anonymous-functions:
Anonymous functions allow you to specify return type, lambdas don't.
If you don't, return type inference works like for normal functions, and not like for lambdas.
As #dyukha said, the meaning of return is different:
A return statement without a label always returns from the function declared with the fun keyword. This means that a return inside a lambda expression will return from the enclosing function, whereas a return inside an anonymous function will return from the anonymous function itself.
There is no implicit it parameter, or destructuring.
Your specific cases will be equivalent, yes.
See the reference: https://kotlinlang.org/docs/reference/lambdas.html
There are several ways to obtain an instance of a function type:
Using a code block within a function literal, in one of the forms:
a lambda expression: { a, b -> a + b },
an anonymous function: fun(s: String): Int { return s.toIntOrNull() ?: 0 }
Both give you a function object which can be used interchangeably