I know .also { } is intended to be used for logging. That is exactly what I want to do.
var count = 1
count++.also { print("count is $it") }
In this example I expect the log to be
count is 2
But it actually prints:
count is 1
Is there a way different way to use also here?
Your problem is not related to also.
You are using the postfix (count++) increment operator which returns the value of a variable before it has been incremented, which is 1.
If you use the prefix increment (++count) operator it will first assign the new value and then return it, in this case 2. Note that you need paratheses in this case:
(++count).also { print("count is $it") }
You mentioned that also is intended for logging. I don't think that is true. It is merelely a scope function which offers the receiver as it (or as you name it) inside the labmda and returns the receiver.
Related
I have a java method to create a constraintstream out of all TeamCalendar objects (planning entity with pinned=true) that do not follow a given Preference.
As a result I would like to have a constraint stream that denotes for each department how many of these discrepancies exist. This is the java code:
private BiConstraintStream<Department, Integer> violatedPreferredTeamDays(ConstraintFactory constraintFactory) {
return constraintFactory.from(TeamCalendar.class)
.ifNotExists(Preference.class,
Joiners.equal(TeamCalendar::getDate, Preference::getDate),
Joiners.equal(TeamCalendar::getDepartment, Preference::getDepartment))
.filter((tc) -> {
return true;
})
.groupBy(TeamCalendar::getDepartment, count())
.filter((dep, count) -> {
return true;
});
}
When running this in Debug mode, the first filter (tc -> {return true;}) is entered a total of 20 times, 5 times for each of the 4 departments.
However, after groupBy, the result set seems to be empty. The debugger does not break code execution in that filter, and the penalize function used on this BiConstraintStream does not penalize anything.
I tried some trivial alternatives:
.groupBy(tc -> tc.getDepartment(), count())
.groupBy(tc -> { return tc.getDepartment(); }, count() )
as well as alternatives for the count method:
.groupBy(TeamCalendar::getDepartment, sum(tc -> 1) )
.groupBy(TeamCalendar::getDepartment, sum(tc -> {return 1;})
But without any improvement.
Am I overlooking something here?
The underlying engine applies the first filter directly on the TeamCalendar and pre-empts the ifNotExists call. Therefore you can not use the filter to check whether or not the ifNotExists has triggered or not.
Constraint Streams are not imperative, and the execution of individual building blocks need not happen in sequence. Constraint Streams can not be inspected using the debugger, unless you understand how the underlying Drools engine evaluates the constraints, and adjust your expectations accordingly.
As to why the groupBy result is empty - probably because the Preference instance actually exists. If that is not the case, please file a JIRA, attach a minimal executable code reproducer, and we will look into it.
Let's say I have the following class:
data class Foo(var name: String) {
operator fun plus(foo: Foo): Foo {
name += foo.name
return this
}
}
Which is then used like this:
val foo1 = Foo("1")
val foo2 = Foo("2")
val foo3 = Foo("3")
foo1+foo2+foo3
println(foo1.name) // 123
Now, what if I wanted different behavior depending on whether the operations are chained like this:
foo1+foo2+foo3
Or like this:
(foo1+foo2)+foo3
In both cases foo1's name would be 123, but let's say that in the second case I would want foo1's name to be (12)3.
Is there a way to add a condition to the plus function, which checks whether the foo that it is called on originates from within parentheses/has a higher precedence or not.
No, that is not possible, because that makes no sense tbh. The compiler will just resolve the order of operations, brackets just indicate that 1+2 should resolve first and the result should be added to 3. There is no concept of brackets anymore in that result, you just have the outcome.
What is confusing you is that you are abusing the plus function to do something people wouldn't expect. You should not use the plus function to mutate the object it is called upon, this is not expected behaviour. Users will expect the plus function to return a new object not a mutation of the left or right operand.
In your case:
operator fun plus(foo: Foo): Foo {
return Foo(name += foo.name)
}
Don't do something different lest you want other people to be really confused. Fyi plusAssign is a mutating function, but still wouldn't allow you to do what you want. To achieve that you'd probably need to write your own parser and parse the operands and operators yourself.
I'm pretty new with Kotlin and I'm trying to figure out Kotlin's scope functions.
My code looks like this:
with(something) {
when {
equals("test") -> var1 = "test123"
startsWith("test2") -> var2 = "test456"
contains("test3") -> myNullableVar?.let { it.var3 = "test789" }
}
}
So before I entered the third check with the .let function my with function does not need to be exhaustive (I'm not returning something, I'm only doing assignments). In my third check I'm using .let as a null-check ... but only for an assignment of it.var3 (if it is not null). I don't need to return anything while I know that Kotlin's .let function returns the result of the body by standard.
Nevertheless now my with/when needs to be exhaustive otherwise it won't compile anymore.
This got me thinking and trying out different things. I found these ways to solve this issue:
I can add an else to my with/when so it becomes exhaustive but actually I don't need an else and I don't want to use it in this case.
I can add another .let, so it looks like this: myNullableVar?.let { it.var3 = "test789" }.let{} .... but this looks kinda hacky to me. Is it supposed to work like this?
Use If(xy==null){...}else{...} stuff but I thought I can solve this with Kotlin differently
Because I'm new with Kotlin I'm not really sure how to handle this case properly. I would probably just go with my second idea because "it works". Or should I don't use .let for null-checks? Add another empty .let{}? Or did I not get the null-safety concept at all? I feel a little bit lost here. Thanks for any help.
This seems to be an unfortunate combination of features…
A when can be non-exhaustive only when it doesn't return a value. The problem is that the with() function does return a value. And since the when is at the bottom, its value is what gets returned, so in this case it must be exhaustive.
So why doesn't it insist on an else branch even if you omit the "test3" branch? That's because assignments don't yield a value. (They evaluate to Unit, which is Kotlin's special type for functions that don't return a useful value.) If every branch gives Unit, then Kotlin seems* to be happy to infer a default branch also giving Unit.
But the "test3" branch returns something else — the type of myNullableVar. So what type does the when infer? The nearest common supertype of that type and Unit, which is the top type Any?. And now it needs an explicit else branch!
So what to do?
You've found a few options, none of which is ideal. So here are a few more, ditto!
You could return an explicit Unit from that branch:
contains("test3") -> { myNullableVar?.let { it.var3 = "test789" }; Unit }
You could return an explicit Unit from the with():
contains("test3") -> myNullableVar?.let { it.var3 = "test789" }
}
Unit
}
You could give an explicit type for the with(). (It has two type parameters, so you'd need to give both, starting with the type of its parameter):
with<String, Unit>("abc") {
I haven't found a single obvious best answer, I'm afraid…
And to answer your last question: yes, ?.let{ is perfectly idiomatic and common for null checks. In this particular case, replacing it with an if happens to solve the type problem:
contains("test3") -> { if (myNullableVar != null) myNullableVar.var3 = "test789" }
But as well as being long-winded, if myNullableVar is a property and not a local variable, then it opens up a race condition (what if another thread sets it to null in between the test and the assignment?) so the compiler would complain — which is exactly why people use let instead!
(* I can't find a reference for this behaviour. Is there an official word on it?)
Using kotlin I can repeat an action in at least two ways:
val times = 5
// First option
for (i in 0 until times) {
print("Action $i")
}
// Second option
repeat(times) {
print("Action $it")
}
I'd like to know the purpose of repeat.
Should the traditional for loop be replaced with repeat function if possible?
Or are there special cases for this function?
Are there any advantages in repeat function?
EDIT
I've made some research about this question. As long as kotlin is open source project, I could download the sources and check git history.
I found that
1) repeat function is a replace for times function extension.
public inline fun Int.times(body : () -> Unit)
2) KT-7074. times function has become deprecated. But why?
It's just a matter of convenience (shortens the code). There are even more ways for example using an IntRange and forEach
(0..4).forEach {
println(it)
}
0 1 2 3 4
They all serve the same purpose, so the choice is yours.
You don't need to worry about performance either, since repeat and forEach are inline functions, which means the lambda code is copied over to the call site at compile time.
Next lines are all just my opinion:
there are no special cases when you should or shouldn't use repeat
function.
it has more concise syntax.
In places where you don't need to manipulate the loop counter or need to repeat only some simple action I would use that function.
It's all up to you to decide when and how to use it.
From Standard.kt:
/**
* Executes the given function [action] specified number of [times].
*
* A zero-based index of current iteration is passed as a parameter to [action].
*
* #sample samples.misc.ControlFlow.repeat
*/
#kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
As you can see repeat(times) is actually for (index in 0 until times).
There is also a zero-based loop counter and it is: it.
Should the traditional for loop be replaced with repeat function if
possible?
I can't find any reason for that
Or are there special cases for this function?
None I can think of.
Are there any advantages in repeat function?
None I can think of, or maybe(?) just 1:
for educational purposes, I suppose it's easier to teach
that repeat(n) { } performs n iterations of the block of statements inside the curly brackets.
I'm trying to use Kotlin's higher-order functions to count how many times a specific element is contained in a list. I have tried something like
val result: Int? = list.count{ list.contains("element") }
but it seems that is returning the size of the list, and I'm not sure why.
You check if the list contains a given element (which will either result in 0 or the list's size) though what you actually want is to count the number of elements that are equal to the given element (not to the whole list).
What you need to do is to change list.contains("element") to it == "element".
The count function works by iterating over the list and checks if the element matches the predicate.
Approximated code
fun <T> Iterable<T>.count(predicate: (T) -> Boolean) {
var count = 0
for (element in this) {
if (predicate(this)) {
count += 1
}
}
return count
}
When you pass list.contains("element") as the parameter then the count function either returns 0 or list.size because it checks the same thing list.size times.
Doing it == "element" though works differently. Instead of checking the whole list you only check the current element. If you are coming from a Java background it might be easier for you to think of it as this: in Java you'd have code looking somewhat like this (element) -> element.equals("element") though in Kotlin if you have a higher-order function with only 1 parameter and don't explicitly name it then it defaults to it. You can read a bit more about it here.