scalascala-catssemigroup

How to add custom Semigroup instance for Int


import cats._
import cats.implicits._

object SemigroupInstances {
  implicit val intSemigroup: Semigroup[Int] = new Semigroup[Int] {
    override def combine(a: Int, b: Int) = a * b
  }
}

import SemigroupInstances._

object Semigroup {
  def apply[T](implicit instance: Semigroup[T]): Semigroup[T] = instance
}

val intSemigroup = Semigroup[Int]

intSemigroup.combine(4, 4) // expected 16, actual 8

So, how to make custom intSemigroup working?


Solution

  • You import two instances of Semigroup[Int]:

    1. via import cats.implicits._
    2. via import SemigroupInstances._

    The compiler chooses the one from cats.implicits._ over your custom instance.

    If you remove that import, it works: https://scastie.scala-lang.org/avsmEturTRGkNEOCDxuXOA

    If you want to know more about how the compiler chooses which implicit instance to use, see this post: Where does Scala look for implicits?

    In your specific case, the reason appears to be that the type of the instance from cats is actually CommutativeGroup[Int]:

    implicit val catsKernelStdGroupForInt: CommutativeGroup[Int] = new IntGroup
    

    Hence, being a derived class (CommutativeGroup extends Semigroup), it is considered more specific and hence preferred.

    If you change the type of your instance to CommutativeGroup[Int] (which is not possible without being mathematically incorrect, but that's not the point here), you get an "Ambiguous given instances" error because the compiler cannot decide which instance to use: https://scastie.scala-lang.org/dzClDOYDSJ20P1SRpH72nA