如何使用Kotlin和Arrow执行程序

如何使用Kotlin和Arrow执行程序,kotlin,functional-programming,io-monad,arrow-kt,Kotlin,Functional Programming,Io Monad,Arrow Kt,我正在尝试使用Kotlin和Arrow学习一些函数式编程,通过这种方式,我已经阅读了一些类似以下的博客:,这很好,我已经理解了主要思想,但在创建程序时,我不知道如何运行它 让我更明确地说: 我有以下代码: typealias EitherIO<A, B> = EitherT<ForIO, A, B> sealed class UserError( val message: String, val status: Int ) { o

我正在尝试使用Kotlin和Arrow学习一些函数式编程,通过这种方式,我已经阅读了一些类似以下的博客:,这很好,我已经理解了主要思想,但在创建程序时,我不知道如何运行它

让我更明确地说:

我有以下代码:

typealias EitherIO<A, B> = EitherT<ForIO, A, B>

sealed class UserError(
        val message: String,
        val status: Int
) {
    object AuthenticationError : UserError(HttpStatus.UNAUTHORIZED.reasonPhrase, HttpStatus.UNAUTHORIZED.value())
    object UserNotFound : UserError(HttpStatus.NOT_FOUND.reasonPhrase, HttpStatus.NOT_FOUND.value())
    object InternalServerError : UserError(HttpStatus.INTERNAL_SERVER_ERROR.reasonPhrase, HttpStatus.INTERNAL_SERVER_ERROR.value())
}


@Component
class UserAdapter(
        private val myAccountClient: MyAccountClient
) {
    @Lazy
    @Inject
    lateinit var subscriberRepository: SubscriberRepository

    fun getDomainUser(ssoId: Long): EitherIO<UserError, User?> {
        val io = IO.fx {
            val userResource = getUserResourcesBySsoId(ssoId, myAccountClient).bind()
            userResource.fold(
                    { error -> Either.Left(error) },
                    { success ->
                        Either.right(composeDomainUserWithSubscribers(success, getSubscribersForUserResource(success, subscriberRepository).bind()))
                    })
        }
        return EitherIO(io)
    }

    fun composeDomainUserWithSubscribers(userResource: UserResource, subscribers: Option<Subscribers>): User? {
        return subscribers.map { userResource.toDomainUser(it) }.orNull()
    }
}

private fun getSubscribersForUserResource(userResource: UserResource, subscriberRepository: SubscriberRepository): IO<Option<Subscribers>> {
    return IO {
        val msisdnList = userResource.getMsisdnList()
        Option.invoke(subscriberRepository.findAllByMsisdnInAndDeletedIsFalse(msisdnList).associateBy(Subscriber::msisdn))
    }
}

private fun getUserResourcesBySsoId(ssoId: Long, myAccountClient: MyAccountClient): IO<Either<UserError, UserResource>> {
    return IO {
        val response = myAccountClient.getUserBySsoId(ssoId)
        if (response.isSuccessful) {
            val userResource = JacksonUtils.fromJsonToObject(response.body()?.string()!!, UserResource::class.java)
            Either.Right(userResource)
        } else {
            when (response.code()) {
                401 -> Either.Left(UserError.AuthenticationError)
                404 -> Either.Left(UserError.UserNotFound)
                else -> Either.Left(UserError.InternalServerError)
            }
        }
    }.handleError { Either.Left(UserError.InternalServerError) }
}
typealias EitherIO=EitherT
密封类用户错误(
val消息:字符串,
val状态:Int
) {
对象身份验证错误:UserError(HttpStatus.UNAUTHORIZED.reasonPhrase,HttpStatus.UNAUTHORIZED.value())
对象UserNotFound:UserError(HttpStatus.NOT\u FOUND.reasonPhrase,HttpStatus.NOT\u FOUND.value())
对象InternalServerError:UserError(HttpStatus.INTERNAL\u SERVER\u ERROR.reasonPhase,HttpStatus.INTERNAL\u SERVER\u ERROR.value())
}
@组成部分
类用户适配器(
私有val myAccountClient:myAccountClient
) {
@懒惰的
@注入
lateinit var subscriberRepository:subscriberRepository
有趣的getDomainUser(ssoId:Long):EitherIO{
val io=io.fx{
val userResource=getUserResourcesBySsoId(ssoId,myAccountClient).bind()
userResource.fold(
{error->one.Left(error)},
{成功->
.right(composeDomainUserWithSubscribers(success,getSubscribersForUserResource(success,subscriberRepository.bind()))
})
}
返回EitherIO(io)
}
fun composeDomainUserWithSubscribers(userResource:userResource,subscribers:Option):用户{
返回subscribers.map{userResource.toDomainUser(it)}.orNull()
}
}
private fun GetSubscriberForUserResource(userResource:userResource,subscriberRepository:subscriberRepository):IO{
返回IO{
val msisdnList=userResource.getMsisdnList()
Option.invoke(subscriberRepository.findallbymsidninddeletedisfalse(msisdnList.associateBy(Subscriber::msisdn))
}
}
私有fun getUserResourcesBySsoId(ssoId:Long,myAccountClient:myAccountClient):IO{
返回IO{
val response=myAccountClient.getUserBySsoId(ssoId)
if(response.issucessful){
val userResource=JacksonUtils.fromJsonToObject(response.body()?.string()!!,userResource::class.java)
或者.Right(userResource)
}否则{
当(response.code()){
401->earth.Left(UserError.AuthenticationError)
404->earth.Left(UserError.UserNotFound)
else->eather.Left(UserError.InternalServerError)
}
}
}.handleError{each.Left(UserError.InternalServerError)}
}
正如您所看到的,它正在将一些结果累积到IO单子中。我应该使用arrow中的
unsafeRunSync()
来运行这个程序,但是在javadoc上声明如下:
**注意**这个函数是用于测试的,它不应该出现在您的主线生产代码中。
我应该提到的是,我知道
不同步
,但在我的例子中,我希望是同步的


谢谢

不要运行
unsafeRunSync
,而应该选择
unsafeRunSync

如果你有
myFun():IO。它是阻塞的,它会导致崩溃,它会导致死锁,它会停止你的应用程序


如果您真的想将IO作为块计算运行,那么您可以在这之前使用
trunt()
来获得
IO。

使用单子,您无法从单子中自由获得单子计算的结果。这是每个单子的基本特征。在一元上下文中,您可以使用一组组合符来处理值,而不是展开值。但是,如果您确切地知道自己在做什么,您可以使用
非同步
,即使在生产代码中也是如此。但这应该是例外,而不是规则。我想提供一个用户作为API调用的结果,但我不知道如何提供用户而不是EitherIO。使您的API挂起并使用
myIO.suspended()
调用它。
myFun().unsafeRunAsync {  /* it (Either<Throwable, List<Int>>) -> */
  it.fold(
    { Log.e("Foo", "Error! $it") },
    { println(it) })
}