Scala method that needs either one of two implicit parameters
Asked Answered
D

3

5

I'm interested if I can create method with similar idea:

def myMethod[T](param: T)(implicit oneOf: Either[TypeClass1[T], TypeClass2[T]]) = oneOf match ...

I've tried to use default parameters (I've seen somethin similar in akka):

def myMethod[T](param: T)(implicit t1: TypeClass1[T] = null, t2: TypeClass2[T] = null) = 
  if (t1 == null) ...

However, that way I cannot force scala compiler to find at least one of them.

Also, I've implemented implicit conversion from TypeClass1[T] to Left[TypeClass1[T], TypeClass2[T]] and from TC2 to Right, however Scala compiler ignores this conversions.

Is there any way to do something like this?

Darceydarci answered 6/4, 2020 at 13:3 Comment(1)
What about having two overloaded methods? But, I guess that could lead to ambiguities, so probably you may also need to mix in implicit priorization.Cosmogony
L
11

The obvious solution is to create a new typeclass that can be constructed using either TypeClass1 or TypeClass2. The new typeclass implements the functionality used by myMethod that is common to both and maps it to the appropriate methods on TypeClass1 or TypeClass2.


Here is an example:

  trait TypeClass1[T] {
    def showOne = println("Typeclass 1")
  }

  trait TypeClass2[T] {
    def showTwo = println("Typeclass 2")
  }

  trait UnionTypeClass[T] {
    def show
  }

  object UnionTypeClass {
    implicit def t1[T](implicit ev: TypeClass1[T]) = new UnionTypeClass[T] {
      def show = ev.showOne
    }

    implicit def t2[T](implicit ev: TypeClass2[T]) = new UnionTypeClass[T] {
      def show = ev.showTwo
    }
  }


  implicit object IntClass extends TypeClass1[Int]
  implicit object StringClass extends TypeClass2[String]


  def myMethod[T](param: T)(implicit ev: UnionTypeClass[T]) = {
    ev.show
  }

  myMethod(0)
  myMethod("hello")

This will print

Typeclass 1
Typeclass 2
Liebig answered 6/4, 2020 at 13:24 Comment(0)
T
6

In Scala 3 you might be able to use union type like so

trait Foo[A]
trait Bar[A]

given foo as Foo[Int] {}

def g[T](using Foo[T] | Bar[T]) = summon
foo[Int] // ok
Tranship answered 6/4, 2020 at 13:33 Comment(3)
Or def g[T: [t] =>> Foo[t] | Bar[t]] = ???Kasey
Or inline def g[T] = scala.compiletime.summonFrom { case _: (Foo[T] | Bar[T]) => ??? }Kasey
Or inline def g[T] = scala.compiletime.summonInline[Foo[T] | Bar[T]]Kasey
K
1

You can use standard shapeless.OrElse or implicitbox.Priority or implicitlogic.Or from one of libraries

https://github.com/milessabin/shapeless

https://github.com/monix/implicitbox

https://github.com/Jasper-M/implicitlogic

def myMethod[T](param: T)(implicit oneOf: OrElse[TypeClass1[T], TypeClass2[T]]) = ???
// def myMethod[T](param: T)(implicit oneOf: Priority[TypeClass1[T], TypeClass2[T]]) = ???
// def myMethod[T](param: T)(implicit oneOf: Or[TypeClass1[T], TypeClass2[T]]) = ???

trait TypeClass1[T]
trait TypeClass2[T]
implicit val tc1: TypeClass1[Int] = ???
implicit val tc2: TypeClass2[String] = ???
myMethod(1) //compiles
myMethod("a") //compiles

Type classes OrElse, Priority are similar to UnionTypeClass from @Tim's answer but they prioritize t1, t2.

Kasey answered 6/7, 2020 at 12:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.