Skip to content

Commit

Permalink
Provide instance of Alternative for Set in alleycats
Browse files Browse the repository at this point in the history
This allows the use of methods from `Alternative` on a `Set` -
for instance, I want to be able to use `separate` on a `Set`:

```
import cats.implicits._
import alleycats.std.set._

val stringsAndInts: Set[Either[String, Int]] = Set(Right(6),Left("Foo"))
val (strings: Set[String], ints: Set[Int]) = stringsAndInts.separate
```

The `Alternative` typeclass just requires `MonoidK` (already provided
for `Set` in `cats-core`) & `Applicative` (already provided for `Set` by
`Monad` in `alleycats`, so adding it to `alleycats` is a small change.

https://github.com/typelevel/cats/tree/main/alleycats-core#set_-instances
  • Loading branch information
rtyley committed Mar 31, 2021
1 parent 3d900de commit 1d5d3ec
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 6 deletions.
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 alleycatsStdInstancesForSet: 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
}

// 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](alleycatsStdInstancesForSet)

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])
}

0 comments on commit 1d5d3ec

Please sign in to comment.