mitch
08/31/2020, 9:40 AMvalue(rs: RandomSource): Sample<A>
). now alot of the code is currently using values (it shows the strikethrough in intellij) and I'm fixing those as well. I realized that there's a fair few functions like this one arb(...)
that wants Sequence in it, how would I go about that?sam
09/01/2020, 8:54 AMarb { n }
fun Arb.Companion.from(f: () -> T): Arb<T>
mitch
09/01/2020, 9:14 AMArb.create
already exist with the correct types, so i wonder if I can just add several more createsArb.choose(vararg pairs: Pair<Int, Arb<A>>)
in kotest
https://github.com/typelevel/scalacheck/blob/master/src/main/scala/org/scalacheck/Gen.scala#L1224-L1232sam
09/01/2020, 9:40 AMmitch
09/01/2020, 9:44 AM[1,2,3]
['a', 'b', 'c']
[]
-> yields []
sam
09/01/2020, 9:44 AMmitch
09/01/2020, 9:46 AMArb.choose
would work. as in, when you do a bind, you'd assign something like weight 1 to the edge cases, and weight 9 to the randomized valuessam
09/01/2020, 9:48 AM[1,2,3]
['a', 'b', 'c']
[] -> we treat this as [random]
mitch
09/01/2020, 9:53 AMsam
09/01/2020, 9:53 AMmitch
09/01/2020, 9:58 AMsam
09/01/2020, 10:02 AMmitch
09/01/2020, 10:03 AMvalue(rs)
i supposesam
09/01/2020, 10:03 AMmitch
09/01/2020, 10:04 AMsam
09/01/2020, 10:04 AMComposedArb
that has extra method(s) for dealing with this kind of thing.mitch
09/01/2020, 10:11 AMsam
09/01/2020, 10:12 AMmitch
09/01/2020, 10:12 AMedgecases(): Exhaustive<A>
(conceptually)sam
09/01/2020, 10:13 AMmitch
09/01/2020, 10:14 AMsam
09/01/2020, 10:15 AMabstract class ComposedArb<out A> : Gen<A> {
fun edgecases(rs: RandomSource): List<A>
abstract fun value(rs: RandomSource): Sample<A>
}
mitch
09/01/2020, 10:17 AMsam
09/01/2020, 10:17 AMmitch
09/01/2020, 10:18 AM(RandomSource) -> List<A>
kleisli?sam
09/01/2020, 10:18 AMsealed class Edgecases<out A> {
abstract fun values(rs: RandomSource): List<A>
class Static(val values: List<A>) : Edgecases<A> {
override fun values(rs: RandomSource): List<A> = values
}
class Dynamic(val fn: (RandomSource) -> List<A>) : Edgecases<A> {
override fun values(rs: RandomSource): List<A> = fn(rs)
}
}
mitch
09/01/2020, 10:19 AMsam
09/01/2020, 10:19 AMfun edges() :EdgeCases<A>
mitch
09/01/2020, 10:21 AM_ -> List<A>
sam
09/01/2020, 10:21 AMabstract class Arb<out A> : Gen<A>() {
abstract fun edges(): Edgecases<A> = Edgecases.Static(edgecases())
abstract fun edgecases(): List<A>
abstract fun values(rs: RandomSource): Sequence<Sample<A>>
companion object
}
mitch
09/01/2020, 10:22 AMsam
09/01/2020, 10:23 AMmitch
09/01/2020, 10:23 AMusers who spent the time converting from 3.x to 4.xi can relate lol
sam
09/01/2020, 10:23 AMtypealias Edgecases = (RandomSource) -> List<A>
mitch
09/01/2020, 10:27 AMdata class Edgecases<A>(edges: (RandomSource) -> List<A>)
i'll get something ready,sam
09/01/2020, 10:27 AMsealed class Edgecases<out A> {
abstract fun values(rs: RandomSource): List<A>
class List(val values: List<A>) : Edgecases<A> {
override fun values(rs: RandomSource): List<A> = values
}
class Randomized(val fn: (RandomSource) -> List<A>) : Edgecases<A> {
override fun values(rs: RandomSource): List<A> = fn(rs)
}
}
mitch
09/01/2020, 10:32 AMSample<A>
and in this case Edgecases<A>
makes it easier to enrich and refactor in the future.sam
09/01/2020, 10:34 AMmitch
09/01/2020, 10:34 AMEdgecases<A>
yet)Edgecases<A>
i'm not entirely convinced with the cartesian product approach, as the size of minimum iterations can explode with the number of edgecase combinations. i.e.
a - [1, 2, 3]
b - [1, 2, 3, 4]
c - [1, 2]
d - [1, 2, 3, 4, 5]
e - [] - randomize 1 element
if we were to compute the product we'll end up with 3 * 4 * 2 * 5 * 1 = 120 minimum iterations.
currently Kotest takes the edgecases linearly due to generate(rs) and iterator.(a, b, c, d, e)
(1, 1, 1, 1, r)
(2, 2, 2, 2, r)
(3, 3, r, 3, r)
(r, 4, r, 4, r)
(r, r, r, 5, r)
Do you mean because of how to handle edgecases? If so, I would just ignore edge cases. They don't really make much sense once you get into compositionquestioning what's the correct thing to do in
bind
and flatMap
case..sam
09/06/2020, 5:03 PMArb.randomOnly()
which returns a copy of the arb but with the edge cases removed (that randomOnly name is a bit lame though) and Arb.withEdgeCases to copy the arb with differnt edge cases.mitch
09/07/2020, 12:34 AMfun generate(rs: RandomSource, edgesSamplingMode: Edgecases.SamplingMode = Edecases.SamplingMode.Exhaustive) = // implementation detail
// i'm making stuff up here
sealed class SamplingMode {
object Exhaustive : SamplingMode() // all exhaustive permutations
// if we want to dirty our generated distribution with edgecases with fixed probability
case class FixedProbabilitySampling(val samplingProbability: Double = 0.1) : SamplingMode()
// if we want more control over the ratio of the distribution
case class DynamicProbabilitySampling(val startProbability: Double = 1.0, val targetProbability = 0.1, val decayFunction: (previousProbability: Double, iterations: Int) -> Double) : SamplingMode()
}
(a, b, c, d, e)
(1, r, 1, 3, r)
(2, 2, 3, 2, r)
(3, 1, r, r, r)
(r, 4, 3, 1, r)
(r, 3, r, 5, r)
It's not as powerful as the exhaustive combinations, because it gives only an approximation of it. If things fail, dev will be presented with the random seed which they can use. I won't be surprised if this is a nice compromise for a system with many inputs.sam
09/07/2020, 12:55 AMmitch
09/07/2020, 3:32 AMAn interesting function to add would bewhich returns a copy of the arb but with the edge cases removed (that randomOnly name is a bit lame though) and Arb.withEdgeCases to copy the arb with differnt edge cases. (edited)Arb.randomOnly()
sam
09/07/2020, 4:07 AMmitch
09/07/2020, 8:00 AM