Dirk
08/23/2022, 12:55 PMraulraja
08/23/2022, 7:16 PMJorge Bo
08/25/2022, 8:53 PMoverride suspend fun invoke(command: Command): Either<BusinessError, Unit> =
either {
val responseOne = serviceOne.call(command.data.first).bind()
val responseTwo = serviceTwo.call(command.data.second).bind()
if (responseOne.amount > responseTwo.amount) {
serviceThree.call()
}
}
Iurysza
08/26/2022, 1:37 PM@Singleton
class AuthUseCase @Inject constructor(
private val networkDataSource: NetworkDataSource,
private val storage: AuthStorage,
) {
suspend fun refreshTokenIfNeeded(): Either<DomainError, Unit> = either {
val token = storage.getToken().bind()
ensure(token.expirationDate.isInTheFuture()) {
TokenExpired
}
}.handleErrorWith { error ->
when (error) {
is TokenNotFound,
TokenExpired,
-> fetchNewTokenAndSaveIt()
else -> error.left()
}
}
private suspend fun fetchNewTokenAndSaveIt(): Either<DomainError, Unit> = either {
networkDataSource.getAccessToken().bind()
}.flatMap { (accessToken, expiresIn) ->
catch {
AuthToken(
accessToken,
expirationDate = nowPlusMillis(expiresIn)
)
}.mapLeft { InvalidExpirationDate }
}.map { storage.putToken(it) }
}
Is the code above arrow
idiomatic?
Anything that can be improved or that is completely off?
Thanks!dimsuz
08/26/2022, 4:33 PMinterface HasAge { val age: Int }
@optics
data class Form(
override val age: Int
): HasAge {
companion object
}
// is exported out of the current "module"
val lens: Lens<HasAge, Int> = Form.age // compilation error
// in another module
fun transformAge(form: HasAge, lens: Lens<HasAge, Int>)
Can i somehow generate lens so that it can be assignable in the val lens
line?
Very likely that all this trouble points to the wrong design on my part, but the thing is that full type information is lost between the modules, I cannot export full data class Form
to be accessible to another module, all I've got is Any
which I can cast to HasAge
and then I thought I'd apply the lens (which also can be exported).Tobias Hermann
08/27/2022, 7:15 PMtuplize
function:
fun foo(x: Int, y: Int) = 3 * x + 2 * y + 1
fun <T, U, R> tuplize(f: (T, U) -> R): ((Pair<T, U>) -> R) = { (a, b): Pair<T, U> -> f(a, b) }
val xs = listOf(Pair(1, 2), Pair(42, 23))
val ys = xs.map(tuplize(::foo))
It works, but I guess arrow-kt already has something nice build-in, and I just can't find it. Can somebody help me out?Lawrence
08/28/2022, 8:57 PMThrowable
that were returned while handling the API. I came up with this code not sure if this is the optimal way to do so:
suspend fun scheduleCall(service: Service, handleError: (Throwable) -> Unit) =
(Schedule.doWhile<Either<Throwable, Data>> { it.isEmpty() }
.logOutput { it.mapLeft { throwable -> handleError(throwable) } })
.repeat { service.call() }
suspend fun callServices(
a: Service,
b: Service
): Either<List<Throwable>, String> {
val errors = mutableListOf<Throwable>()
val winner =
raceN(
<http://Dispatchers.IO|Dispatchers.IO>,
{ scheduleCall(a) { errors += it } },
{ scheduleCall(b) { errors += it } },
{ delay(60.seconds) })
return when (winner) {
is First -> "A".right()
is Second -> "B".right()
is Third -> errors.left()
}
}
Gavin Ray
08/30/2022, 5:46 PMarrow-meta
repo, does anyone know whether this went anywhere?
Would love to have proper pattern matching in Kotlin -- even Java has it now! 😅
https://mattmoore.io/kotlin-pattern-matchingJoão Gabriel Zó
08/30/2022, 9:50 PMJavier
08/31/2022, 2:16 PMKev
09/01/2022, 7:50 AMEither.resolve
extension function that is commented as “resolving some suspended function” however, the function parameter is not of a suspended type. Would this be a problem with the docs, the implementation or my understanding?
https://github.com/arrow-kt/arrow/blob/main/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt#L1175-L1199Gopal S Akshintala
09/01/2022, 4:15 PMmitch
09/01/2022, 8:38 PMList<Either<A,B>>
to two separate lists of As and Bs in an arrow way”?
That partitionEithers
looks awesome and it’s nice that it works with any Foldable, but is a bit tough to grok. Anything new available in the last 3 years?carbaj0
09/03/2022, 6:24 AMfranztesca
09/03/2022, 8:29 PMControlThrowable
(which is not a CancellationException
), in the PR with the inline fold the shift throws a ShiftCancellationException
, which is a CancellationException
... Therefore Arrow 2 is going to have a different behavior when interacting with the Kotlin coroutines, as stated here (and the conclusion of that was "It seems that continuing to use ControlThrowable
for shifting/short-circuiting is the best option."). Why have you chosen to use a CancellationException
instead of ControlThrowable
?
Question 2: It seems to me that the new solution (the inline fold and ShiftCancellationException
implementing the short circuit) is much simpler than the one implemented using continuations. Why did you choose the continuation approach in the first place and is there any drawback with the simpler throw/catch ShiftCancellationException
?
Thank you!Alex Yordanov
09/05/2022, 3:57 AMcallbackFlow
in Either
? I came up with that but it looks kind of funky:
val flow = callbackFlow<Unit> {
delay(2000)
cancel(CancellationException("Boom"))
}
Either.catch {
flow
.catch { throw RuntimeException(it.message) }
.collect()
}.mapLeft { it.message?.let { msg -> FlowError.Canceled(msg) } }
Masrur Mirboboev
09/05/2022, 8:15 PMtraverse
seems to short circuit on the first failure.Lukasz Kalnik
09/08/2022, 12:43 PMEither
with other wrappers? I have a MutableStateFlow<Either<DataMissing, T>>
and want to update it only if it's a `Right`:
val automations: MutableStateFlow<Either<DataMissing, AutomationList>>
private fun handleEvent(event: EntityModel) {
if (event is AutomationUpdatePendingStateEvent) {
automations.update { automationsEither ->
automationsEither.map { automations ->
automations.updateAutomationState(
automationId = event.automationId,
pendingState = event.deviceState,
provisioningComplete = event.provisioningComplete
)
}
}
}
}
Archie
09/09/2022, 2:50 AMEither
and both have to be right
for me to do proper computation?Stylianos Gakis
09/09/2022, 9:22 AMreturn either<Error, Unit> {
parZip(
{ someComputation().toEither() },
{ someComputation().toEither() },
) { one, two ->
one.bind()
two.bind()
}
}
When I simply want two things to run in parallel, get the structured concurrency guarantees of cancelling each other if one fails, and simply get an answer of it both succeeded or not.
I feel like the parZip is kinda of redundant since I’m not combining the two results in any way, and could even move the bind()
inside the two lambdas and then the trailing lambda is useless.
Is there some other part of the arrow API that I’m missing, or might as well go with doing the async();async();awaitAll()
dance myself?Jorge Bo
09/09/2022, 8:45 PMthanh
09/12/2022, 9:25 AMmapLeft
in the new EffectScope
?
context(EffectScope<Throwable>)
fun count(): Int = TODO()
sealed interface DomainError
context(EffectScope<DomainError>)
fun logic(): Int {
// I want to map Throwable to DomainError here
return count()
}
Fiouz
09/13/2022, 5:53 AMjava.lang.AssertionError: java.net.SocketException: Software caused connection abort: recv failed is of type java.net.SocketException but expected java.net.ConnectException
Kristian Nedrevold
09/13/2022, 6:40 PMfun foo(): Either<Err, Ok>
val res = foo().map { bar(it) }.mapLeft { return it }
and foo() is Err?Gavin Ray
09/16/2022, 9:16 PMInline classes with generic underlying type (experimental)
feature can improve some wrapper classes in Arrow, right?
In a few places in the source, I think I recall seeing data class Foo<T>(val value: T)
Gavin Ray
09/16/2022, 9:17 PM.refaster
for writing automated refactoring code to do stuff like this)Neil Miller
09/20/2022, 3:24 AMraulraja
09/20/2022, 4:14 PMVille Peurala
09/21/2022, 6:44 AMTower Guidev2
09/21/2022, 1:15 PMTower Guidev2
09/21/2022, 1:15 PMprivate suspend fun actualWork(): Either<Any, Any> = either {
val networkData = repository.pharmaDocumentsSearch(options = mapOf("text" to "*", "limitation.count" to "100")).tapLeft { networkError -> consumeNetworkError(workerName, networkError) }.bind()
Either.catch { repository.database.documentSearchItemDAO().insertAsync(manufacture(networkData)) }.tapLeft { databaseError -> consumeDatabaseError(workerName, databaseError) }.bind()
}
fun consumeNetworkError(caller: String, exception: CallError) {
when (exception) {
is HttpError -> doLogging { "$caller Network Http error :: ${exception.code} ${exception.message} ${exception.body}" }
is IOError -> doLogging(exception.cause) { "$caller Network IO error" }
is UnexpectedCallError -> doLogging(exception.cause) { "$caller Unexpected Network error" }
}
}
which all works really nicelyactualWork
function fails silentlyoverride suspend fun pharmaDocumentsSearch(headers: Map<String, String>, options: Map<String, String>): Either<CallError, DocumentsSearch> =
withContext(NETWORK) { service.pharmaDocumentsSearch(headers = HEADERS, options) }
Either.catch { }