1

I'm working on updating some Kotlin code that depends on arrow-kt. As part of the migration process from arrow 1.2 to 2.x, I need to get rid of arrow partiallyN() functions and replace them with native Kotlin lambdas.

The functions I am working on look like this:

private val simpleResultsProcessor: (MutableList<String>?, String) -> List<String> = { results, row ->
    if (results == null) {
        val newResults = mutableListOf<String>()
        newResults.add(row)
        newResults
    } else {
        results.add(row)
        results
    }
}

private  fun select(sql: List<String>, resultsProcessor: (String) -> List<String>): Option<List<String>>
{
    var result = none<List<String>>()
    sql.forEach {
        result = resultsProcessor(it).toOption()
    }
    return result
}

The real code is more complicated, but this simple list builder demonstrates the behavior I am struggling with.

I then have a test function that calls the select() function using an arrow partiallyN and a Kotlin lambda.

@Test
fun `test partiallyN replacement`()
{
    val res1 = select(listOf("AB", "CD", "EF"), simpleResultsProcessor.partially1(mutableListOf()))
    val res2 = select(listOf("AB", "CD", "EF") ) { sql-> simpleResultsProcessor(mutableListOf(), sql)}

    assertThat(res1.isSome()).isTrue
    assertThat(res1.getOrNull()).containsExactly("AB", "CD", "EF")
    //res1 == ["AB", "CD", "EF"]
    assertThat(res2.isSome()).isTrue
    assertThat(res2.getOrNull()).containsExactly("AB", "CD", "EF")
    //res2 == ["EF"]
}

I would expect both calls to produce the same results, however the lambda is overwriting the existing values rather than adding to them. What am I missing here? What is the partially function doing special and how do I write a lambda that will replicate that behavior?

1 Answer 1

1

For res1 you create a mutableListOf() only once which is then used to create the resultsProcessor function. This function is then called repeatedly on each entry of the sql list, but it will always be used the same MutableList.

That is different for res2: Here, the mutableListOf() is placed inside of the lambda which will be executed for each entry of the sql list. So for each entry, a new MutableList will be created.

The quick fix would be to move mutableListOf() out of the lambda so it is only executed once, maybe like this:

val initialList = mutableListOf<String>()
val res2 = select(sql = listOf("AB", "CD", "EF")) { sql ->
    simpleResultsProcessor(initialList, sql)
}

However, I would recommend to refactor select and simpleResultsProcessor instead. I'm not sure what you actually want to achieve with it, but passing MutableLists around which are then modified as a side effect and which are then also returned makes your code hard to read and prone to errors: You never know what lists are the same and what changes affect which lists.

The cleanest solution would be to switch to immutable lists. If that doesn't work for you (in most cases it does, so don't dismiss this too quickly), you should at least establish a clear ownership over the MutableList. Then only that owner controls the modifications, making it more predictable what happens to it.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.