I’m still very new to the language and trying to m...
# arrow
r
I’m still very new to the language and trying to make sense of that https://arrow-kt.io/docs/effects/io/#suspend-r---a In other languages, “Persistence” would be a private variable inside the class, injected via constructor… and now in that example, Persistence and Repo are… “merged” together via delegation. What about method name conflicts, or even exposing things from Persistence that should remain hidden? What if I’m trying to model en “environment” where I need to have multiple “Repo”s?
c
I'm not an expert, but I don't think persistence is a very important concept in this example, don't overthink it. The example is trying to show that Either is really good to model errors and failures, without Exceptions
r
Yeah, I understand that, was just trying to find a nice way to do dependency injection without using a DI container
a
If you like the environment model there are many ways you can do it. For instance using the following as a model where you can explicitly define the requirements for your environment at the method level and then at your call site (edge of your world) you can create your environment however you want to:
Copy code
data class User(
    val username: String
)

interface HasUserRepository {
    fun UserRepository(): UserRepository
}

suspend fun <ENV> ENV.loadUser(name: String): Either<StudioError, User>
        where ENV : HasUserRepository = either {
    userRepository().findOneByName(name).bind()
}
For instance if we create a concrete implementation named ApiEnvironment that provides the HasUserRepository trait we can then have a query endpoint such as (dataFetcherResult translates the result or error to an appropriate result, using the error handler if necessary):
Copy code
class UserQuery(env: ApiEnvironment) {
    suspend fun user(username: String): DataFetcherResult<User> = 
        env.loadUser(username).dataFetcherResult(errorHandler)
}
and a simple implementation of the environment:
Copy code
interface ApiEnvironment : HasUserRepository {
    companion object {
        operator fun invoke(): BundleQueryEnvironment {
            return object : ApiEnvironment,
                HasUserRepository by HasUserRepository(UserRepositoryImplementation()) {}
        }
    }
}
Then if you utilize spring, koin, or any other DI framework you can create an implementation utilizing that library, if desired as well.
☝️ 1
🔝 1
r
And what do you believe to be the “recommended” way to do DI in kotlin in an application using Arrow? Should I just pick Koin (or another container) and call it a day?
By the way, thank you for that answer, it’s really good 🙂 Just wondering if that approach would be idiomatic in a arrow + kotlin project
a
I'm not sure I could say what is idiomatic at this point, perhaps when arrow meta is more refined that may provide some utilities that become idiomatic. If it were for myself, if I don't have any dependencies that require a DI framework, I'd probably create a concrete implementation of the ApiEnvironment.
c
A better way to write your last block:
Copy code
interface ApiEnvironment : HasUserRepository {
    companion object : ApiEnvironment, HasUserRepository by UserRepositoryImplementation()
}
a
Depends on how you construct your implementations, that syntax becomes painful if you have more complex constructions.