我有一些方法的调用链,在这里我通过Kleisli传递上下文。基本上,我想将上下文传递给db访问层,但是我想在两者之间的任何地方访问此上下文。

以下示例可以完美运行。我的问题是,我也想访问OrderService.findAll(...)中的上下文。我尝试了几种方法,但一直失败。

object OrderRepository {
    fun findAll(userId: String): Kleisli<Context, ForIO, List<Order>> =
        Kleisli { ctx ->
            IO {
                ctx.db.query("someQuery")
            }
        }
}

object OrderService {
    fun findAll(userId: String): Kleisli<Context, ForIO, List<OrderResponse>> =
         OrderRepository.findAll(userId).map(IO.monad()) { orderList ->
            orderList.map {
                //Create OrderResponse from Order
            }
        }
}

是否可以访问那里的上下文,或者这没有任何意义吗?谢谢您的帮助 :)

最佳答案

您需要的是从D到D的Kleisli,以D为上下文。这样,您还可以将D(上下文)作为结果类型,并且可以flatMap并访问它。这就是为什么ask()提供的方法可以在随播广告中使用的原因。

假设您的OrderRepository也是Context中的依赖项,而不是纯函数(出于示例目的),因此您需要从服务的上下文中访问它。看到:

interface OrderApi
interface OrderDB {
  fun query(query: String): List<Order> = TODO()
}

data class Order(val id: String)
data class OrderResponse(val order: Order)
data class Context(val api: OrderApi, val repository: OrderRepository, val db: OrderDB)

class OrderRepository {
  fun findAll(userId: String): Kleisli<Context, ForIO, List<Order>> =
    Kleisli { ctx ->
      IO {
        ctx.db.query("someQuery")
      }
    }
}

object OrderService {
  fun findAll(userId: String): Kleisli<Context, ForIO, List<OrderResponse>> {
    val monad = IO.monad()
    return Kleisli.ask<Context, ForIO>(monad).flatMap(monad) { ctx ->
      ctx.repository.findAll(userId).map(monad) { orderList ->
        orderList.map { OrderResponse(it) }
      }
    }
  }
}

就是说,Kleisli是Monad的转换器(也称为ReaderT),使用起来有些麻烦。如果要在功能代码库上注入(inject)依赖项并保持简单,我的建议是在Context接收器上使用扩展功能,该接收器已经隐式地在所有级别上传递了依赖项,这在on this post by Paco中进行了描述。

08-18 17:35
查看更多