diff --git a/kernel/src/main/scala/cats/kernel/Semigroup.scala b/kernel/src/main/scala/cats/kernel/Semigroup.scala index e05407fdde..d299d35f48 100644 --- a/kernel/src/main/scala/cats/kernel/Semigroup.scala +++ b/kernel/src/main/scala/cats/kernel/Semigroup.scala @@ -1,7 +1,7 @@ package cats.kernel import scala.{ specialized => sp } -import scala.annotation.{ tailrec } +import scala.annotation.tailrec /** * A semigroup is any set `A` with an associative operation (`combine`). @@ -76,4 +76,11 @@ object Semigroup extends SemigroupFunctions[Semigroup] { * Access an implicit `Semigroup[A]`. */ @inline final def apply[A](implicit ev: Semigroup[A]): Semigroup[A] = ev + + /** + * Create a `Semigroup` instance from the given function. + */ + @inline def instance[A](cmb: (A, A) => A): Semigroup[A] = new Semigroup[A] { + override def combine(x: A, y: A): A = cmb(x, y) + } } diff --git a/tests/src/test/scala/cats/tests/SemigroupSuite.scala b/tests/src/test/scala/cats/tests/SemigroupSuite.scala index fc5771bcda..c29a081b2a 100644 --- a/tests/src/test/scala/cats/tests/SemigroupSuite.scala +++ b/tests/src/test/scala/cats/tests/SemigroupSuite.scala @@ -2,10 +2,10 @@ package cats package tests import org.scalatest._ +import org.scalatest.prop.GeneratorDrivenPropertyChecks - -class SemigroupSuite extends FunSuite { +class SemigroupSuite extends FunSuite with Matchers with GeneratorDrivenPropertyChecks { { import cats.implicits._ Invariant[Semigroup] @@ -19,4 +19,14 @@ class SemigroupSuite extends FunSuite { Semigroupal[Semigroup] InvariantMonoidal[Semigroup] } + + test("Semigroup.instance creates a Semigroup from the given function") { + val mult: (Int, Int) => Int = (a, b) => a * b + val add: (Int, Int) => Int = (a, b) => a + b + + forAll { (a: Int, b: Int) => + Semigroup.instance(mult).combine(a, b) should === (a * b) + Semigroup.instance(add).combine(a, b) should === (a + b) + } + } }