https://kotlinlang.org logo
#arrow
Title
# arrow
d

dave08

03/28/2024, 10:19 AM
Is it so terrible to have a
var
when multiple functions return enriched data on the same structure:
Copy code
var result = getFoo()
result = enrich1(result)
result = enrich2(result)
...
?
s

Sam

03/28/2024, 10:22 AM
I'd rather use `let`:
Copy code
val result = getFoo()
  .let(::enrich1)
  .let(::enrich2)
Or even `fold`:
Copy code
val result = listOf(::enrich1, ::enrich2)
  .fold(getBar()) { intermediate, enrich ->
    enrich(intermediate)
  }
d

dave08

03/28/2024, 10:25 AM
Yeah, nice point if those enrich functions only take the result of the last step! In my case, some of the enrich functions take more params... is it worth it to work hard to stuff them into lambda lists or something... or just use
var
?
j

Johann Pardanaud

03/28/2024, 10:27 AM
You could use
var
, it's not that terrible, however
let
is a good option I think, you can even use it with more params and keep your code quite clean:
Copy code
val result = getFoo()
  .let { enrich1(it, param2) }
  .let(::enrich2)
👍🏼 1
s

Sam

03/28/2024, 10:27 AM
You can always use the longer form
let { enrich1(it, otherStuff) }
if needs be. But I don't have anything against a good old-fashioned
var
if it helps to make the intent clear.
💯 3
🫰
d

dave08

03/28/2024, 10:28 AM
Yeah, good point! I guess in a sense, sometimes it's harder to see that the result of the step before is being used for the next one, and
let { }
makes that clearer. Thanks!
I always only used let for nullable guards, I didn't realize that this could be a nice usage of it too!
The only not-so-nice thing about let in my case is that the result of enrich is added to the old result... which makes this a nice candidate for fold... but not when the parameters are different....
Copy code
val result = getFoo().let { it + enrich(...) }....
I wonder if that's as readable... if I would use var, I could just use
+=
...
j

Johann Pardanaud

03/28/2024, 11:06 AM
then use
var
, it's okay 🙂 or you could also do the following to obtain a
val
at the end:
Copy code
val result = run {
    var builder = "hello"
    builder += " world"
    builder
}

assert(result == "hello world")
👍🏼 1
1
a

Alejandro Serrano.Mena

03/28/2024, 12:59 PM
I would say that if the functions are enriching
result
, they should directly mutate it instead of returning a copy that you're then "smashing"
Copy code
var foo = getFoo().apply {
  enrich1(it)
  enrich2(it)
}
d

dave08

03/28/2024, 1:24 PM
That's what I did originally... but first, it makes me have to create mutable structures in Foo, and second, doesn't that go against the whole immutability principle in functional programming?
a

Alejandro Serrano.Mena

03/28/2024, 1:28 PM
it depends, sometimes you need to strike a balance between copying and mutability. I think the "always immutable" is a good general rule, but the really important one is "controlled mutability". For example, if you use a
var
inside a function because it makes the code cleaner/more efficient, that's perfectly fine, as you are not contaminating other. Having said so, for big data structures which are refined by steps having variables is a common pattern. For example, inside the Kotlin compiler this is how it works: the parsed program is a big tree, and different phases (e.g., type inference) change some data (in the example of type inference, it changes
null
in the type to the inferred one). This avoids the need to copy several times, and (in that specific case) would also allow to transform separate parts of the tree concurrently
d

dave08

03/28/2024, 4:04 PM
Well, for efficiency, I'm trying to use kotlinx.collections.immutable... but you're right, if it has a limited scope, it might be better to keep mutability. My problem is that it started like that, and then started getting out of hand and being used in a not-so-limited scope...