You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Kotlin与Arrow构建函数结果集合且不使用可变集合?

如何使用Kotlin与Arrow构建函数结果集合且不使用可变集合?

嘿,这个问题我太有共鸣了——用可变列表凑活确实能跑,但总觉得和函数式编程的“不变性”原则格格不入,对吧?别担心,Arrow-KT专门为这种“批量操作+收集所有错误”的场景提供了完美的函数式解决方案,完全不用碰可变集合!

核心思路:用Validated替代Either实现错误累积

你现在用的Either是“快速失败”的——一旦遇到第一个Left就会停住,但我们需要的是收集所有失败结果,这时候Arrow的Validated类型就派上用场了。它天生支持错误累积,正好匹配我们的需求。

具体步骤

假设我们先定义好错误类型(让错误更清晰):

sealed class OperationError {
    data class DeleteError(val item: String, val reason: String) : OperationError()
    data class UpdateError(val item: String, val reason: String) : OperationError()
    data class AddError(val item: String, val reason: String) : OperationError()
}

你的CRUD函数返回Either<OperationError, Unit>,比如:

fun deleteItem(item: String): Either<OperationError, Unit> = TODO()
fun updateItem(item: String): Either<OperationError, Unit> = TODO()
fun addItem(item: String): Either<OperationError, Unit> = TODO()

1. 把Either转成Validated,开启错误累积模式

Arrow提供了toValidated()扩展函数,可以轻松把Either转换成Validated:

import arrow.core.toValidated
import arrow.core.traverse
import arrow.core.Validated
import arrow.core.NonEmptyList

// 处理删除集合:遍历每个元素,转成Validated并累积错误
val deleteResults: Validated<NonEmptyList<OperationError>, List<Unit>> =
    deleteItems.traverse { item ->
        deleteItem(item).toValidated()
    }

// 同理处理更新和添加集合
val updateResults: Validated<NonEmptyList<OperationError>, List<Unit>> =
    updateItems.traverse { item ->
        updateItem(item).toValidated()
    }

val addResults: Validated<NonEmptyList<OperationError>, List<Unit>> =
    addItems.traverse { item ->
        addItem(item).toValidated()
    }

这里的traverse函数会自动帮我们遍历集合,把每个元素的结果收集起来,而且不会因为某个失败就停止——所有错误都会被累积到NonEmptyList里。

2. 合并三个集合的结果,统一收集所有错误

用Arrow的zip方法可以把三个Validated结果合并成一个,这样所有错误都会被汇总:

import arrow.core.zip

val combinedResult: Validated<NonEmptyList<OperationError>, Triple<List<Unit>, List<Unit>, List<Unit>>> =
    deleteResults.zip(updateResults, addResults)

3. 提取错误列表(或处理成功结果)

最后我们可以通过fold方法提取所有错误,或者处理成功的情况:

val allErrors: List<OperationError> = combinedResult.fold(
    { errors -> errors.toList() }, // 如果有错误,转成普通List
    { emptyList() } // 所有操作成功的话,返回空列表
)

另一种极简方式:手动用fold累积错误(不用Validated)

如果你暂时不想引入Validated,也可以用纯Either配合fold来实现,虽然不如前者优雅,但也是纯函数式:

fun processAllOperations(
    deleteItems: List<String>,
    updateItems: List<String>,
    addItems: List<String>
): List<OperationError> {
    // 处理删除操作,收集错误
    val deleteErrors = deleteItems
        .map { deleteItem(it) }
        .fold(emptyList()) { acc, either ->
            acc + either.leftOrNull().orEmpty()
        }
    
    // 同理处理更新和添加操作
    val updateErrors = updateItems
        .map { updateItem(it) }
        .fold(emptyList()) { acc, either ->
            acc + either.leftOrNull().orEmpty()
        }
    
    val addErrors = addItems
        .map { addItem(it) }
        .fold(emptyList()) { acc, either ->
            acc + either.leftOrNull().orEmpty()
        }
    
    return deleteErrors + updateErrors + addErrors
}

为什么这比可变集合好?

  • 完全符合函数式编程的不变性原则,没有副作用
  • 类型更安全:Validated的NonEmptyList能明确告诉我们“如果是错误状态,至少有一个错误”
  • 代码更简洁,Arrow的工具函数帮我们省去了手动管理集合的麻烦

这样一来,你就能彻底抛弃可变列表,用优雅的函数式方式完成错误收集啦!

备注:内容来源于stack exchange,提问作者pbuchheit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 09:23:08