cats-effect icon indicating copy to clipboard operation
cats-effect copied to clipboard

Laws testing for queue functor instances

Open TimWSpence opened this issue 4 years ago • 6 comments

We have {Functor, Invariant, Contravariant} instances for {Q, Deq, PQ}ueue in cats.effect.std but they currently have no laws tests. We should correct this :)

TimWSpence avatar Jan 22 '21 17:01 TimWSpence

I'll try to add this during the next week

patseev avatar Feb 21 '21 09:02 patseev

I'll try to add this during the next week

Awesome, thanks!! :)

TimWSpence avatar Feb 21 '21 13:02 TimWSpence

Started fiddling with it, and already have a couple of questions. Maybe you guys could help me with direction.

First, for law-tests I need Eq[Queue[F, A]] instance. I suppose I can derive it by taking all elements from Q/Deq/PQueue and comparing the lists. Does that make sense here?

Second, I need a Gen instance for Queue[F, A] and I suppose that I want to generate real instances - Bounded, Unbounded, Dropping, CircullarBuffer. Creation of these is effectful (cuz of Ref) and I'm ending up with Gen[IO[Queue[IO, A]]] and don't know how to solve this.

patseev avatar Feb 21 '21 19:02 patseev

Started fiddling with it, and already have a couple of questions. Maybe you guys could help me with direction.

First, for law-tests I need Eq[Queue[F, A]] instance. I suppose I can derive it by taking all elements from Q/Deq/PQueue and comparing the lists. Does that make sense here?

Second, I need a Gen instance for Queue[F, A] and I suppose that I want to generate real instances - Bounded, Unbounded, Dropping, CircullarBuffer. Creation of these is effectful (cuz of Ref) and I'm ending up with Gen[IO[Queue[IO, A]]] and don't know how to solve this.

Excellent questions! I think for laws/property testing we at some point have to depend on unsafeRun. If you've looked at TestInstances you may have already seen that we have

  implicit def eqIOA[A: Eq](implicit ticker: Ticker): Eq[IO[A]] =
    Eq.by(unsafeRun(_))

so I think you can piggy-back on this to get

//loop with tryTake until you get None
def toList[A](q: Queue[IO, A]): IO[List[A]] = ???

implicit def eqForQueue[A: Eq]: Eq[Queue[IO, A]] = Eq.by(q => toList(q))

As for Gen, I think I'd be inclined to pull a similar trick:

def fromList[A](as: List[A]): IO[Queue[IO, A]] = Queue.empty[IO, A].flatMap(q => as.traverse(a => q.offer(a)))

def genQueue[A: Arbitrary]: Gen[Queue[IO, A]] =
  arbitrary[List[A]].map(as => fromList(as).unsafeRunSync())

Does that make sense? Of course someone else might have a better idea! 😂

TimWSpence avatar Feb 22 '21 09:02 TimWSpence

Thank you! Managed to make it work 🙂

patseev avatar Feb 22 '21 10:02 patseev

@patseev I don't think I noticed a PR connected to this issue. Since it sounds like you got it to work, would you be interested in submitting a PR? Thanks! :)

armanbilge avatar Apr 07 '22 09:04 armanbilge