Skip to content

Instantly share code, notes, and snippets.

@PaNaVTEC
Last active October 26, 2022 06:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PaNaVTEC/8b4b198affeb3bbb3ac6afe7d636ffe2 to your computer and use it in GitHub Desktop.
Save PaNaVTEC/8b4b198affeb3bbb3ac6afe7d636ffe2 to your computer and use it in GitHub Desktop.
Scala 3 - Arbitrary typeclass derivation
package me.panavtec.scalacheck
import org.scalacheck.Arbitrary
import org.scalacheck.Gen
import scala.compiletime.{erasedValue, summonInline}
import scala.deriving.*
object ArbitraryDeriving:
inline def summonAll[T <: Tuple]: List[Arbitrary[?]] =
inline erasedValue[T] match
case _: EmptyTuple => Nil
case _: (t *: ts) => summonInline[Arbitrary[t]] :: summonAll[ts]
inline given derived[T](using m: Mirror.Of[T]): Arbitrary[T] =
val elemInstances = summonAll[m.MirroredElemTypes]
inline m match
case s: Mirror.SumOf[T] => deriveSum(s, elemInstances)
case p: Mirror.ProductOf[T] => deriveProduct(p, elemInstances)
def deriveSum[T](s: Mirror.SumOf[T], elems: List[Arbitrary[?]]): Arbitrary[T] =
Arbitrary(
Gen
.chooseNum(0, elems.size - 1)
.flatMap(elems(_).arbitrary.asInstanceOf[Gen[T]])
)
def deriveProduct[T](p: Mirror.ProductOf[T], elems: List[Arbitrary[?]]): Arbitrary[T] =
Arbitrary(
elems
.foldRight[Gen[Tuple]](Gen.const(EmptyTuple)) { (arb: Arbitrary[?], acc: Gen[Tuple]) =>
arb.arbitrary.flatMap { gen => acc.map(gen *: _) }
}
.map(p.fromProduct(_))
)
final case class SomeProduct(
id: String,
name: String,
color: Color
)
enum Color:
case Red
case Blue
object SomeProduct:
val f: SomeProduct = summon[Arbitrary[SomeProduct]].sample.get
given Arbitrary[Red.type] = ArbitraryDeriving.derived
given Arbitrary[Blue.type] = ArbitraryDeriving.derived
given Arbitrary[Color] = ArbitraryDeriving.derived
given Arbitrary[SomeProduct] = ArbitraryDeriving.derived
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment