Marc
04/22/2025, 10:01 AMkotest-extensions-arrow
have ben move to read only. could someone tell me what i the alternative we have to use now? šš»Emil Kantis
04/22/2025, 2:18 PMAlejandro Serrano.Mena
04/22/2025, 7:08 PMRaise
https://arrow-kt.io/community/blog/2025/04/22/arrow-intellij-0-3/Artūras Šlajus
04/23/2025, 9:08 AMval v1: Validator<E1>
val v2: Validator<E2>
listOf(v1, v2): List<Validator<E1 | E2>>
Fred Friis
04/25/2025, 3:36 PMsome kotlin runtime libraries have an unsupported binary format
some kotlin runtime libraries and 6 other jars have an unsupported binary format
my understanding is this repo has kotlin 1.9 so what's the dealio?aykrieger
04/26/2025, 2:27 AMinit{ }
block and this would throw an exception if the data given in the constructor was invalid.
Example:
@JvmInline
value class Foo(
val bar: String,
) {
init {
require(bar.isNotBlank())
}
}
I was thinking I could use the Either
class and a companion object to validate the data when calling create
without throwing an exception.
@JvmInline
value class Foo(
val bar: String,
) {
companion object {
fun create(bar: String): Either<Error, Foo> =
if (bar.isNotBlank()) {
Foo(bar).right()
} else {
IsBlankString().left()
}
}
}
}
sealed interface Error {
class IsBlankString: Error
}
The issue is that I can't make the Foo
constructor Foo(val bar: String)
private for value class
or data class
. So I can't guarantee that the property bar
has been validated if someone creates Foo
with Foo("")
instead of Foo.create("")
.
I tried changing the function name from create
to invoke
to possibly override the constructor but it seems the Kotlin compiler picks the constructor Foo(val bar: String)
instead of the invoke
function when calling Foo("")
.
I understand I can achieve this with a regular class
and use a private constructor, but I haven't been able to achieve this with a value class
or data class
. I would like have the auto-generated copy()
and equals()
functions that data classes
provides.
I read this article and they have an interesting solution for this issue by using a sealed interface
to validate a data class
. When I tried this solution, I wasn't able to use the .copy()
function normally auto-generated for data class
. So there are some tradeoffs.
Link:
https://proandroiddev.com/how-to-use-arrows-either-for-exception-handling-in-your-application-a73574b39d07
Does anyone have any recommendations or solutions you have used to solve this issue?Alen Mujezinovic
04/29/2025, 2:57 PMprivate constructor
and @ConsistentCopyVisibility?
There are cases where copy
should only be available to other methods of the class and therefore be private, e.g. when modelling state changes that require $otherThings to happen before copy
can be safely called.Alejandro Serrano.Mena
04/29/2025, 3:26 PMcarbaj0
04/30/2025, 5:54 AMYoussef Shoaib [MOD]
04/30/2025, 9:29 AMAutoCloseScope
can do reverse-mode automatic differentiation:
interface AD<Num> {
val Double.num: Num
val Int.num: Num get() = toDouble().num
operator fun Num.plus(other: Num): Num
operator fun Num.times(other: Num): Num
fun exp(x: Num): Num
}
data class NumB(val value: Double, var d: Double)
fun backwardsAutoClose(x: Double, prog: AD<NumB>.(NumB) -> NumB): Double {
val input = NumB(x, 0.0)
autoCloseScope {
val res = object : AD<NumB> {
override val Double.num: NumB get() = NumB(this, 0.0)
override fun NumB.plus(other: NumB) = NumB(value + other.value, 0.0).also { z ->
onClose {
this.d += z.d
other.d += z.d
}
}
override fun NumB.times(other: NumB) = NumB(value * other.value, 0.0).also { z ->
onClose {
d += other.value * z.d
other.d += value * z.d
}
}
override fun exp(x: NumB): NumB {
val xExp = mathExp(x.value)
val z = NumB(xExp, 0.0)
onClose { x.d += xExp * z.d }
return z
}
}.prog(input)
res.d += 1
}
return input.d
}
gpopides
04/30/2025, 11:53 AMEdgar Avuzi
05/02/2025, 6:10 AMYoussef Shoaib [MOD]
05/03/2025, 11:55 PMinline fun <A> doubleNegationElimination(block: ((A) -> Nothing) -> Nothing): A = merge { block(this::raise) }
For more info
Bonus: suspend
allows the same thing (which is precisely why early raise builders were made using suspend
magic):
suspend fun <A> doubleNegationElimination(block: suspend (suspend (A) -> Nothing) -> Nothing): A = suspendCoroutine { cont ->
block.startCoroutine({ value ->
suspendCoroutine<Nothing> {
cont.resume(value)
}
}, cont)
}
Marc
05/07/2025, 2:34 PMcontext(raise: Raise<DomainError>)
fun <T> HttpClient.getSomethingFromNetwork(): T = with(raise) {
// network operator | raise(DomainError)
}
not sure if thatās the only way tho , but it feels odd the need to scope raise now šStephen Morse
05/15/2025, 2:53 PMNonEmptyList
(using NonEmptyListSerializer
) between Arrow v1 and v2? My company (Cash App/Block) has some data serialized with the NonEmptyListSerializer
of Arrow v1, but we need to upgrade to Arrow v2. Is it possible to define a serializer that can deserialize both the v1 and v2 binary formats? š¤dawidhyzy
05/16/2025, 8:15 AMcontext(_: Raise<StreamError>, _ : Raise<ServiceError.Expected>)
suspend fun getLiveStream(url: ChannelStreamUrl): Stream
Alejandro Serrano.Mena
05/16/2025, 2:36 PMGeert
05/20/2025, 7:40 AMperson.copy {
Person.names.last transform { it.replaceFirstChar(Char::uppercase) }
}
Of course the last
val does not exist, but is there some way to do this?jean
05/23/2025, 10:50 AMEither
does, didnāt it?Youssef Shoaib [MOD]
05/25/2025, 8:27 PMRaise
can be used to implement first-class generic lambdas (i.e rank-2 polymorphism)!
import arrow.core.raise.Raise
import arrow.core.raise.merge
private fun main() {
println("Identity:")
useIdentity {
raise.raise(value)
}
println("ListMaker: ")
useListMaker {
// "opening" an existential type
// ideally, this would work like the example above through compiler magic
fun <A> ListMaker<A>.block(): Nothing = raise.raise(listOf(value))
block()
}
useListMaker {
fun <A> ListMaker<A>.dishonest(): Nothing = when (value) {
is Unit -> raise.raise(listOf())
is Int -> raise.raise(listOf(value))
else -> raise.raise(listOf(value, value))
}
dishonest()
}
println("Choice: ")
useChoice {
raise.raise(second)
}
useChoice {
fun <A> Choice<A>.dishonest(): Nothing = when (first) {
is Unit -> raise.raise(second)
is Int -> raise.raise(first)
else -> raise.raise(second)
}
dishonest()
}
}
private class Identity<A>(val value: A, val raise: Raise<A>)
private fun useIdentity(block: Identity<*>.() -> Nothing) {
val value = merge {
Identity(Unit, this).block()
}
println(value)
val value2 = merge {
Identity(42, this).block()
}
println(value2)
}
private class ListMaker<A>(val value: A, val raise: Raise<List<A>>)
private fun useListMaker(block: ListMaker<*>.() -> Nothing) {
// we can convert this to a List<Unit>, which is equivalent to an Int
val listValue = merge {
ListMaker(Unit, this).block()
}
println(listValue)
val listValue2 = merge {
ListMaker(42, this).block()
}
println(listValue2)
// We can always keep the polymorphic function honest by wrapping in an unreachable type
// Using data class for clarity, but ideally you want a type that returns the same
// toString and same hashCode for all instances.
data class Wrapper<A>(val value: A)
val wrappedValue = merge {
ListMaker(Wrapper(Unit), this).block()
}
println(wrappedValue)
// hence it can't know the type of the value inside without reflection
// (but with reflection, all bets are off anyway)
val wrappedValue2 = merge {
ListMaker(Wrapper(42), this).block()
}
println(wrappedValue2)
}
private class Choice<A>(val first: A, val second: A, val raise: Raise<A>)
private fun useChoice(block: Choice<*>.() -> Nothing) {
// we can convert this to a Boolean
val value = merge {
Choice(false, true, this).block()
}
println(value)
val value2 = merge {
Choice(41, 42, this).block()
}
println(value2)
// Keeping it honest, just like in ListMaker
data class Wrapper<A>(val value: A)
val wrappedValue = merge {
Choice(Wrapper(false), Wrapper(true), this).block()
}
println(wrappedValue)
// so results are identical here
val wrappedValue2 = merge {
Choice(Wrapper(41), Wrapper(42), this).block()
}
println(wrappedValue2)
}
It turns out we can very easily implement generic lambdas (i.e. lambdas like <A> (A) -> List<A>
) without direct language support! Of course, we could already use a (not-fun
) interface ListMaker { fun <A> make(a: A): List<A> }
, but that'd require making an anonymous object at the call site, which has awkward syntax, and cannot be made inline
. Instead, we can define it directly by giving the lambda only one way to exist, which is to raise
something of an existentially-quantified type, while also potentially giving it several members of that unknown type.
Sadly, the compiler isn't super smart at the moment in dealing with existential types. It can handle some of them in very, very simple cases (as shown in the useIdentity
example), but it gives up whenever there's any function call in between (like listOf
, for instance). Hopefully the compiler can get smarter in the future!jean
05/30/2025, 7:28 AMparTraverse
? I see the migration guide recommands mapOrAccumulate
but does it have the parrallelle
capabilities too?dave08
06/05/2025, 4:39 PMJ.D. Satlin
06/10/2025, 12:42 AMrecover
. Sometimes this is a sign to turn a function into one that takes a raise context overall, but sometimes that feels less advisable (public functions, that then become less discoverable because they require the raise context, mostly. That might be its own discussion). About the simplest version I've got is below, but I'm wondering if there's just something I'm missing in how to do this more easily than a flatmap?
fun randomNumber(): Either<String, Int> {
val randomNum = Random(1).nextInt(1..10)
return if (randomNum > 5) {
randomNum.right()
} else {
"The number was too low".left()
}
}
fun failOnMoreConditions(): Either<String, Int> {
return randomNumber()
.flatMap { num ->
either {
ensure(num != 10) { "Number 10 also not allowed" }
num
}
}
}
S.
06/11/2025, 3:04 PMeither {}
in combination with flow {}
? when I have a function like this
fun list(): Either<Error, Flow<Item>> = either {
flow {
if (someSuspendFun()) this@either.raise(Error)
emitAll(someList())
}
}
it says arrow.core.raise.RaiseLeakedException: 'raise' or 'bind' was leaked outside of its context scope. Make sure all calls to 'raise' and 'bind' occur within the lifecycle of nullable { }, either { } or similar builders.
or is the way to go to make the function suspend and do the check before the flow block?Erik Dreyer
06/12/2025, 4:19 PMEither.catch()
I find myself wanting to treat it like either {}
and use various DSL methods provided by Raise
inside the catch() function. Since it doesn't provide that context, is this the correct way?
either {
Either.catch() { ... }.bind()
}
Kjartan
06/25/2025, 5:50 AMKev
06/30/2025, 6:28 AMMichael Friend
07/01/2025, 6:30 PMblock: context(Raise<Error>) () -> T
parameter, calling the function within an either
builder causes some ambiguity between the extension function raise variants like Raise<E>.raise()
you can call on the receiver on the either
block and the context(Raise<E>) raise()
variant available on the context parameter on block which makes it easy to raise an error on the either block rather than within the block parameterOlaf Gottschalk
07/08/2025, 3:18 PMServer
- which is defined as an interface
. So it looks along the lines of this:
interface Server {
fun prepare()
fun run()
fun shutdown()
}
In order to properly handle these as resources, I have some fun execute()
that does this:
fun execute() {
resourceScope {
servers.forEach {
install({ it.prepare() }, { _, _ -> it.shutdown() })
}
servers.forEach(Server::run)
}
}
Now, this assumes that all Server
classes basically can do all of there setup in prepare
and properly release all resources in shutdown
.
In some servers though, I would like to also use the resource concept and install things in the same resource scope to allow them to live alongside the servers and properly get shutdown as well. Those types of server implementations typically do not define any code in their shutdown
function, but they want to install more resources in the same resource scope.
That's why I extended my prepare
fun like this:
interface Server {
context(ResourceScope) fun prepare()
fun run()
fun shutdown()
}
To now execute my system, I need to manually bring the resource scope back into scope:
fun execute() {
resourceScope {
servers.forEach {
install({ context(this@resourceScope) { it.prepare() } }, { _, _ -> it.shutdown() })
}
servers.forEach(Server::run)
}
}
Now the big question: am I doing something wrong? Is it safe to hand in the same resource scope into the acquire step of a resource so this resource can also install resources in the same scope?
Note: the reason I do this is that I got several different types of Server implementations, some that need a "classic" prepare/shutdown step, others that are fine with installing resources!raulraja
07/17/2025, 5:19 PM