Предоставляя Полугруппу экземпляров классов типов и инфиксный оператор +
через метод расширения, например
trait Semigroup[A] {
def combine(x: A, y: A): A
}
case class Foo(v: Int)
case class Bar(a: String, b: String)
implicit val fooSemigroup: Semigroup[Foo] = (x: Foo, y: Foo) => Foo(x.v + y.v)
implicit val barSemigroup: Semigroup[Bar] = (x: Bar, y: Bar) => Bar(x.a + y.a, x.b + y.b)
implicit class SemigroupOps[A](x: A) {
def +(y: A)(implicit ev: Semigroup[A]): A = ev.combine(x, y)
}
Foo(41) + Foo(1) // res0: Foo = Foo(42)
Bar("He", "wo") + Bar("llo", "rld") // res1: Bar = Bar(Hello,world)
Теперь ваш метод может быть ограничен примерно так
def f[A](first: A, second: A)(implicit ev: Semigroup[A]): Unit =
println(first + second)
Этот ответ пытается просто проиллюстрировать концепцию, поэтому на практике прислушиваются к комментариям Луиса и используют готовые средства для кошек.