frees-io / iota

Fast [co]product types with a clean syntax. For Cats & Scalaz.
Apache License 2.0
177 stars 22 forks source link

Error when projecting a scalacheck Arbitrary Coproduct #187

Open juanjovazquez opened 6 years ago

juanjovazquez commented 6 years ago

A java.lang.ClassCastException is raised when projecting a sample Coproduct obtained through a scalacheck Arbitrary. Example:

import iota.{ Cop, TNil }
import iota.TList.::
import iota.scalacheck._
import org.scalacheck.{ Arbitrary, Gen }

object IotaScalacheck {

  object Monday
  object Tuesday
  object Wednesday
  object Thursday
  object Friday

  type Weekday = Cop[
    Monday.type ::
    Tuesday.type ::
    Wednesday.type ::
    Thursday.type ::
    Friday.type ::
    TNil
  ]

  implicit val arbMonday    = Arbitrary(Gen.const(Monday))
  implicit val arbTuesday   = Arbitrary(Gen.const(Tuesday))
  implicit val arbWednesday = Arbitrary(Gen.const(Wednesday))
  implicit val arbThursday  = Arbitrary(Gen.const(Thursday))
  implicit val arbFriday    = Arbitrary(Gen.const(Friday))

  def main(args: Array[String]): Unit = {
    val weekday: Weekday = implicitly[Arbitrary[Weekday]].arbitrary.sample.get

    val MondayInject    = Cop.Inject[Monday.type, Weekday]
    val TuesdayInject   = Cop.Inject[Tuesday.type, Weekday]
    val WednesdayInject = Cop.Inject[Wednesday.type, Weekday]
    val ThursdayInject  = Cop.Inject[Thursday.type, Weekday]
    val FridayInject    = Cop.Inject[Friday.type, Weekday]

    weekday match { // runtime error! java.lang.ClassCastException: org.scalacheck.ArbitraryLowPriority$$anon$1 cannot be cast to IotaScalacheck$Thursday$
      case MondayInject(day)    => println(day)
      case TuesdayInject(day)   => println(day)
      case WednesdayInject(day) => println(day)
      case ThursdayInject(day)  => println(day)
      case MondayInject(day)    => println(day)
      case FridayInject(day)    => println(day)
    }
  }
}
andyscott commented 6 years ago

I think this is a mistake on my part: https://github.com/frees-io/iota/blob/84a417bab5585b1ac45a84f2c40bcfab4ba43f66/modules/scalacheck/src/main/scala/iota/scalacheck/package.scala#L18

gens should be renamed to arbitraries then unsafe untyped line needs to be adjusted to get the Gen by calling .arbitrary on the arbitrary instance. Ideally we can add some tests to ensure this works correctly.

I can take a stab at this later this week.