Marius Kotsbak
10/28/2021, 11:26 AM@Suppress("ClassName")
@Deprecated(
"The `either` computation block supports validated with the right short-circuiting semantics",
ReplaceWith("either", "arrow.core.computations.either")
)
object validated {
inline fun <E, A> eager(crossinline c: suspend RestrictedValidatedEffect<E, *>.() -> A): Validated<E, A> =
Effect.restricted(eff = { RestrictedValidatedEffect { it } }, f = c, just = { it.valid() })
suspend inline operator fun <E, A> invoke(crossinline c: suspend ValidatedEffect<E, *>.() -> A): Validated<E, A> =
Effect.suspended(eff = { ValidatedEffect { it } }, f = c, just = { it.valid() })
}
As far as I can see .bind() short circuit on first validation error instead of accumulating?:
suspend fun <B> Validated<E, B>.bind(): B =
when (this) {
is Validated.Valid -> a
is Validated.Invalid -> control().shift(this@bind)
}
simon.vergauwen
10/28/2021, 11:38 AMvalidated { }
block which was confusing because it didn’t accumulate errors but rather behaved like either { }
that’s why we deprecated it.
If you want to bind
Validated like an either
, you can do it inside the either { }
block.Marius Kotsbak
10/28/2021, 11:38 AMMarius Kotsbak
10/28/2021, 11:39 AMsimon.vergauwen
10/28/2021, 11:41 AMsequence
or traverse
.Marius Kotsbak
10/28/2021, 11:52 AMsimon.vergauwen
10/28/2021, 12:05 PMzip
.
Sine when you combine all errors, you need to somehow be able to combine all values as well.
ollect
cIf the values are all of the same type, they can safely be combined into a collection type.Marius Kotsbak
10/28/2021, 12:52 PMvalidated<Nel<Error>, Int> {
val a = 5.valid().bind()
val b = "10".valid().bind()
Error("test").invalid().bind()
a + b.toInt()
}
Marius Kotsbak
10/28/2021, 12:55 PMsimon.vergauwen
10/28/2021, 1:24 PMvalidated<Nel<Error>, Int> {
val a: Int = Error("test").invalid().bind()
val b = "$a".valid().bind()
Error("test2").invalid().bind()
a + b.toInt()
}
You can never run "$a"
because you never receive a
.mitch
10/28/2021, 10:08 PMmitch
10/28/2021, 10:10 PMmitch
10/28/2021, 10:13 PMsimon.vergauwen
10/29/2021, 7:02 AMflatMap
I’d say. In the case of Validated
it doesn’t exist, and we can only implement it with the same behavior as Either
which is “give me the value, or short-circuit if you don’t have it”.
So in that case, the continuation cannot accumulate because it doesn’t have a value to continue its computation and exists.mitch
10/29/2021, 7:03 AMsimon.vergauwen
10/29/2021, 7:03 AMArb
this isn’t the case though, but rather you extract values from the Arb
for a given RandomSource
right?mitch
10/29/2021, 7:09 AMArb<A>.bind()
implemented as basically flatMaps through arbitrary { }
syntax. This is going to be available in 5.x. https://github.com/kotest/kotest/blob/master/kotest-property/src/commonMain/kotlin/io/kotest/property/arbitrary/builders.kt#L355-L410mitch
10/29/2021, 7:13 AMcheckAll(arbA, arbB, arbC) { }
that’s like zip / applicative so it’s trivial to compute shrinks.
however with flatmaps, we may have arbC
dependent on arbB
, dependent on arbA
simon.vergauwen
10/29/2021, 7:15 AMsimon.vergauwen
10/29/2021, 7:16 AMsimon.vergauwen
10/29/2021, 7:17 AMarbC
depend on when bind()
is called on it 🤔mitch
10/29/2021, 7:18 AMsimon.vergauwen
10/29/2021, 7:22 AMfun Arb.Companion.sum(x: Int, y: Int): Int = Arb.constant(x + y)
checkAll {
val x = <http://Arb.int|Arb.int>().bind()
val y = Arb.long().bind().toInt()
val sum = Arb.sum(x, y).bind()
}
At the point of bind()
you know:
1. We’re binding <http://Arb.int|Arb.int>()
and we’ve never bound another Arb
before. We can keep track of the values we’ve seen here, and we know the IntShrinker
.
2. Arb.long()
we know that it’s the second Arb
and we can again keep track of all the values + LongShrinker
.
3. Custom Arb, we know the values that come by but there is not Shrinker
.
==> If you want to shrink this prop-test, you want to re-run the lambda by binding all the values for x
and y
based on the last values we saw + `IntShrinker`/`LongShrinker`, right?
So we have all information available to do that, no?simon.vergauwen
10/29/2021, 7:23 AMsimon.vergauwen
10/29/2021, 7:23 AM<http://Arb.int|Arb.int>()
though, it’s based on a RandomSource
right?mitch
10/29/2021, 8:29 AMMap<ArB<*>, RTree<*>>
must exist for all the arbs. On shrinking-mode we can then juxtapose random generated values with shrunk values. i.e.
// this works, because these val arbs has a consistent reference
val arbInt = <http://Arb.int|Arb.int>()
val arbLong = Arb.long()
checkAll {
val x = arbInt.bind()
val y = arbLong.bind().toInt()
val sum = Arb.sum(x, y).bind()
}
// this does not work, because each arb occupies different reference
checkAll {
val x = <http://Arb.int|Arb.int>().bind()
val y = Arb.long().bind().toInt()
val sum = Arb.sum(x, y).bind()
}
I really wish there’s a way to define Equal<Arb<*>>
I’m running out of ideas because essentially it’s a function holder…simon.vergauwen
10/29/2021, 8:59 AMArb
is which one.simon.vergauwen
10/29/2021, 9:00 AMMarius Kotsbak
10/29/2021, 12:21 PM