Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide instance of Alternative[Set] in alleycats #3837

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions alleycats-core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package alleycats
package std

import alleycats.compat.scalaVersionSpecific._
import cats.{Always, Applicative, Eval, Foldable, Monad, Monoid, Traverse, TraverseFilter}
import cats.{Alternative, Always, Applicative, Eval, Foldable, Monad, Monoid, Traverse, TraverseFilter}

import scala.annotation.tailrec

object set extends SetInstances

@suppressUnusedImportWarningForScalaVersionSpecific
trait SetInstances {
// This method advertises parametricity, but relies on using
// Monad advertises parametricity, but Set relies on using
// universal hash codes and equality, which hurts our ability to
// rely on free theorems.
//
Expand All @@ -30,8 +30,12 @@ trait SetInstances {
// contain three. Since `g` is not a function (speaking strictly)
// this would not be considered a law violation, but it still makes
// people uncomfortable.
implicit val alleyCatsStdSetMonad: Monad[Set] =
new Monad[Set] {
//
// If we accept Monad for Set, we can also have Alternative, as
// Alternative only requires MonoidK (already accepted by cats-core) and
// the Applicative that comes from Monad.
implicit val alleyCatsStdSetMonad: Monad[Set] with Alternative[Set] =
new Monad[Set] with Alternative[Set] {
def pure[A](a: A): Set[A] = Set(a)
override def map[A, B](fa: Set[A])(f: A => B): Set[B] = fa.map(f)
def flatMap[A, B](fa: Set[A])(f: A => Set[B]): Set[B] = fa.flatMap(f)
Expand Down Expand Up @@ -65,6 +69,10 @@ trait SetInstances {
go(f(a))
bldr.result()
}

override def empty[A]: Set[A] = Set.empty

override def combineK[A](x: Set[A], y: Set[A]): Set[A] = x | y
Comment on lines +73 to +75
Copy link
Contributor Author

@rtyley rtyley Mar 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are duplicated from catsStdInstancesForSet in cats-core:

def empty[A]: Set[A] = Set.empty[A]
def combineK[A](x: Set[A], y: Set[A]): Set[A] = x | y

...I wasn't sure if delegating to the cats-core typeclass instance was the thing to do, or if it was clearer just to duplicate!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with this. The duplication is slight and the laws ought to catch problems.

}

// Since iteration order is not guaranteed for sets, folds and other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ package alleycats.tests

import alleycats.laws.discipline._
import alleycats.std.all._
import cats.Foldable
import cats.{Alternative, Foldable}
import cats.instances.all._
import cats.kernel.laws.discipline.SerializableTests
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.{ShortCircuitingTests, TraverseFilterTests}
import cats.laws.discipline.{AlternativeTests, ShortCircuitingTests, TraverseFilterTests}

class SetSuite extends AlleycatsSuite {
implicit val iso: Isomorphisms[Set] = Isomorphisms.invariant[Set](alleyCatsStdSetMonad)

checkAll("FlatMapRec[Set]", FlatMapRecTests[Set].tailRecM[Int])

checkAll("Foldable[Set]", SerializableTests.serializable(Foldable[Set]))

checkAll("TraverseFilter[Set]", TraverseFilterTests[Set].traverseFilter[Int, Int, Int])

checkAll("Set[Int]", AlternativeTests[Set].alternative[Int, Int, Int])
checkAll("Alternative[Set]", SerializableTests.serializable(Alternative[Set]))

checkAll("Set[Int]", ShortCircuitingTests[Set].traverseFilter[Int])
}