0
votes

I'm trying to make a typeclass that depends on user input. Imagine we have some case objects:

sealed trait H
case object Ha extends H
case object Hb extends H

and the type class:

trait Foo[A] {
    def bar: String
}

object Foo {
    def bar[A : Foo] = implicitly[Foo[A]].bar

    implicit object FooA extends Foo[Ha.type] {
        override def bar: String = "A"
    }

    implicit object FooB extends Foo[Hb.type] {
        override def bar: String = "B"
    }
}

While I found a working solution using a match:

variableComingFromMainArgs match {
  case "a" => Foo.bar[Ha.type] _
  case "b" => Foo.bar[Hb.type] _
}

I remember that we have abstract types in Scala, so I could change my case class into:

sealed trait H {
    type T <: H
}

case object Ha extends H {
    type T = this.type
}

case object Hb extends H {
    type T = this.type
}

Now, when depending on user input to the program, I could do something like val variable = Ha println(Foo.bar[variable.T])

However, for some reason this doesn't work the and the error is not very useful for me:

error: could not find implicit value for evidence parameter of type Foo[variable.T]
        println(Foo.bar[variable.T])

Any ideas if this can be overcome, if not, why?

Thanks.

1

1 Answers

1
votes

Implicits are compile time constructs so in principle they cannot depend on user input directly (programmer can wire it for example with pattern matching as you did).

Consider the following code. It compiles and works as intended:

trait H {
    type A
}

case object Ha extends H {
    override type A = Int
}

case object Hb extends H {
  override type A = Long
}

trait Adder[T] {
  def add(a: T, b: T): T
}

implicit object IntAdder extends Adder[Int] {
  override def add(a: Int, b: Int): Int = a + b
}

implicit object LongAdder extends Adder[Long] {
  override def add(a: Long, b: Long): Long = a + b
}

def addWithAdder(input: H)(a: input.A, b: input.A)(implicit ev: Adder[input.A]): input.A = ev.add(a, b)

val x: Int = addWithAdder(Ha)(3, 4)
val y: Long = addWithAdder(Hb)(3, 4)

Let's focus on addWithAdder method. Thanks to path dependent types compiler can choose correct implicit for this task. But still this method is basically the same as the following:

def add[T](a: T, b: T)(implicit ev: Adder[T]) = ev.add(a, b)

The only advantage first one can have is that you can provide all instances yourself and stop the user of your code to add own types (when H is sealed and all implementations are final).