Mateu
02/09/2024, 3:26 PMUlrich Schuster
02/15/2024, 10:36 AMEither.catch{..}
and the raise
DSL? I'm having the following problem:
either {
zipOrAccumulate(
{ ensureNotNull(<some function>) { MyErrorType1 },
{ ensureNotNull(<some other function>) { MyErrorType2 },
{ Either.catch { MyEnum.valueOf(someString) }.mapLeft { MyErrorType3 }
) { a, b, c -> MyType(a, b, c)
}
The Either.catch
block does not type check, because the result is not compatible with the raise
DSL. What is the idiomatic way to do it?Kev
02/17/2024, 3:29 AMgetQuestionById
to call getNodeById
with a given lambda which can do the cast. However, the lambda that will be past in requires a context of type Raise<NonEmptyList<StructuralError>>
and getByNodeId
has the context Raise<JourneyError>
context(JourneyErrors)
suspend fun <T : Node> getNodeById(
id: StructuralNodeId,
transformer: (StructuralNode) -> EitherNel<StructuralError, T>,
): Pair<StructuralNodeId, T> {
val node = repository.getStructuralNodeById(id)
val transformed = transformer(node).mapLeft { errors ->
val message = errors.joinToString { error -> "${error.message}\n" }
JourneyError.Fatal(message).also {
logger.error("Failed to getNodeById: $message")
}
}.bind()
return Pair(id, transformed)
}
context(JourneyErrors)
suspend fun getQuestionById(id: StructuralNodeId): Pair<StructuralNodeId, Question> =
getNodeById(id) {
// Complaining about no context for Raise<NonEmptyList<StructuralError>>
Question(it.attributes)
}
I tried doing something with the transformation parameter definition to be something like transformer: context(NonEmptyList<StructuralError>) (StructuralNode> -> EitherNel<StructuralError, T>
which just ends up with the transformer
method invocation requiring 2 parameters (NonEmptyList<StructuralError>, node).
How can I apply a context receiver on a lambda method parameter?Kev
02/17/2024, 3:42 AM0xf1f1
02/17/2024, 8:27 PMdimsuz
02/19/2024, 4:02 PMList<Either<Throwable, Int>>
what's the best way to filter out only Right
values and then get a List<Int>
discarding left values? I can do a filter + map
, but is there 2 in 1 option available?
UPD. I have found that I can do list.mapNotNull { it.getOrNull() }
Emil Kantis
02/20/2024, 8:57 PMfold
that's only applicable for Either<E, Unit>
? Basically I want to do handle potential error-case or unpack it so we don't have a dangling Either.0xf1f1
02/23/2024, 8:53 AMfun mkReport(res: Response): EitherNel<Error, SomeType> = either {
when (res.success) {
true -> return SomeType()
false -> {
val ee: List<String> = res.errors
return ee.map { Error(it) }
.toNonEmptyListOrNull()!!
}
}
}
The false condition fails with the following error:
Type mismatch.
Required:
EitherNel<Error, SomeType>
Found:
NonEmptyListErrorFilip Piechowski
02/23/2024, 10:50 AMNorbi
02/24/2024, 11:36 AMsindrenm
02/27/2024, 5:54 PMWesley Hartford
02/29/2024, 12:13 AMjava.lang.IllegalStateException: Returning a lazy computation or closure from 'fold' breaks the context scope, and may lead to leaked exceptions on later execution.
Make sure all calls to 'raise' and 'bind' occur within the lifecycle of nullable { }, either { } or similar builders.
See Arrow documentation on 'Typed errors' for further information.
Is this error new in version 1.2.2? I haven't changed any code other than the arrow upgrade. It seems like it's trying to tell me that I'm doing something like this:
fun bad() = either {
{ calledLazily().bind() }
}
Which is bad because the bind
may be called outside of the either
scope it implicitly refers to. Unfortunately, I cannot see what part of my code is doing anything like that.simon.vergauwen
02/29/2024, 10:10 AMsindrenm
02/29/2024, 6:42 PM@optics
issues I discovered the other day, and congrats on getting 1.2.3 out! I'm afraid I've found one more super edge case, however, that still generates bad Kotlin code. Filed it upder https://github.com/arrow-kt/arrow/issues/3384. Sorry, not sorry. 😅Davio
03/01/2024, 7:58 AMNathan Bedell
03/02/2024, 5:44 PMFree (Map a)
?
I want to say my gut intuition is "no" as the Map a
branching would probably require multi-shot continuations, but I thought I'd ask anyway.Ulrich Schuster
03/04/2024, 3:30 PMLaurent Thiebaud
03/04/2024, 3:39 PMFred Friis
03/04/2024, 9:55 PMFred Friis
03/06/2024, 1:56 AMChris Lee
03/06/2024, 2:26 AMNick Kleban
03/06/2024, 8:51 AMMervyn McCreight
03/06/2024, 4:07 PMEmil Kantis
03/07/2024, 8:47 AMensureEmpty(list) { nel -> ProblemWithNel(nel) }
Fred Friis
03/12/2024, 4:19 PMkotlin
return taxableOrder
.copy(
checks = taxableOrder
.checks
?.map { taxableCheck ->
taxableCheck.copy(selections = taxableCheck
.selections
?.map { taxableSelection ->
taxableSelection.copy(taxCategory = TaxableSelection.TaxCategory.HOT_FOOD) //this is really the only thing we want to change
}
)
}
)
// but we still have to map and copy every step on the way into that nested attribute 🫠Fred Friis
03/12/2024, 6:10 PMNavneet
03/13/2024, 8:42 AMianbrandt
03/14/2024, 3:50 AMfun interface Converter<in I, out O> {
fun convert(input: I): O
}
My system does a lot of domain mapping, so this interface has 100+ implementations.
Some Converter
implementations are trivial, and are declared to return non-nullable or nullable types. Others can result in logical errors, and so I've been declaring them to return an `Either`:
object NonTrivialConverter : Converter<Int, Either<Error, Int> {
fun convert(input: Int): Either<Error, Int> = either {
ensure(input <= LIMIT) { Error() }
input * SCALING_FACTOR
}
}
I'm currently using the `either`/`bind()` approach to compose logic that calls such functions, but it's not ideal:
• The extra bind()
calls increase verbosity.
• They can be omitted without producing a compile-time error, which is contrary to the shift-left value proposition of typed errors.
◦ I'm aware of the Detekt rule, but additional build and IDE plugins add overhead and risk compared to native language and IDE support.
• The ergonomics of lambdas as function bodies isn't great.
◦ For one, there's the jarring mix of value-of-last-expression returns for lambda function bodies vs. explicit `return`s for regular function bodies.
◦ Also, at least in the latest version of IntelliJ, return type issues in lambdas result in the entire lambda body being underlined in red, unlike the precise error highlighting in regular function bodies.
I've experimented with the Raise
DSL. That wonderfully addresses all of the above issues, with one problem. If I understand correctly, to use the Raise DSL with my generic converters I need to update my functional interface to have a Raise
receiver or context parameter, e.g.:
context(Raise<Error>)
fun interface Converter<in I, out O> {
fun convert(input: I): O
}
The issue is that I've now colored my functional interface, not unlike if I'd made it `suspend`ing. I can't migrate my codebase to it incrementally, and all implementers must be called from a Raise
context regardless of whether they actually need it.
Have you all encountered this problem, and is there a solution for it besides sticking with the `either`/`bind()` approach?
If that latter is the only option, I'd start providing feedback on the code coloring downside of the current context parameters proposal, or advocating for the revisiting of for-comprehensions in Kotlin (KT-18861) as a native solution to `either`/`bind()`.Kev
03/18/2024, 9:04 AMgeorge.m
03/18/2024, 7:49 PMfun calculateDoubles(calcParams: Params) =
calcParams
.let { params ->
defrombulate(params.first, param.second)
.let { result ->
gaussianRoots(result)
.let { grtts ->
storeResult(params.first, params.second, result, grtts)
}
}
}
and do this instead:
fun calculateDoubles(calcParams: Params) =
// chained with let()
doLet(
{ calcParams },
{ params -> defrombulate(params.first, param.second) },
{ params, result -> gaussianRoots(result) },
{ params, result, grtts -> storeResult(params.first, params.second, result, grtts) }
)
Although in my code I have returns of types Result
or Either
.
I have tried looking on the Arrow docs but couldn't find an example of what I am looking for?
Could someone point me to some examples or documentation?
But I may be mistaken, and Comprehensions is not the way I should go about this.