0

After watching a youtube video with the title Scala Type Members vs Type Parameters. I wrote the following.

  1. Purely Type parameter version works fine

     trait Joiner[Elem,R] {
         def join(xs: Seq[Elem]): R
     }
    
    
    
    object Program {
    
      def doJoin[T,R] (xs: T *) (implicit j: Joiner[T,R] ): R = j.join (xs)
    
      def main(args: Array[String]): Unit = {
    
    
        implicit val charToStringJoiner = new Joiner[Char,String] {
          override def join(xs: Seq[Char]): String = xs.mkString("+")
        }
        implicit val charToInt = new Joiner[Char,Int] {
          override def join(xs: Seq[Char]): Int = xs.mkString.toInt
        }
    
    
        val s:String = doJoin[Char,String]('1','2')
        println(s)
        val n :Int = doJoin[Char,Int]('1','2')
        println(n)
    
      }
    
    }
    
  2. Mixed Type Member & Parameter version -

       trait Joiner[Elem] {
          type R
          def join(xs: Seq[Elem]): R
        }
    
    
        object Program {
    
         def doJoin[T] (xs: T *) (implicit j: Joiner[T] ): j.R = j.join (xs)
    
          def main(args: Array[String]): Unit = {
    
            implicit val charToStringJoiner = new Joiner[Char] {
              override type R = String
              override def join(xs: Seq[Char]): String = xs.mkString("+")
            }
    
            implicit val charToInt = new Joiner[Char] {
              override type R = Int
              override def join(xs: Seq[Char]): Int = xs.mkString.toInt
            }
    
            val s:String = doJoin('1','2') //doesn't work
            println(s)
            val n :Int = doJoin('1','2') //doesn't work
            println(n)
    
          }
    
        }
    

Version 1 is fine however version 2 doesn't compile. How can this be fixed while having both implicit in scope? Specifically how can I specify the return type which will help the compiler resolve the correct implicit

lawal
  • 952
  • 10
  • 19
  • I don't think it can be done. Under most circumstances the compiler can only disambiguate on the first parameter list. – jwvh Jul 21 '17 at 08:07

2 Answers2

0

The issue is that you have two implicit val with the same types Joiner[Char] in the scope. Split them to different function and it should work:

object Program {

  def doJoin[T] (xs: T *) (implicit j: Joiner[T] ): j.R = j.join (xs)

  def main(args: Array[String]): Unit = {

    def do1: Unit ={
      implicit val charToStringJoiner = new Joiner[Char] {
        override type R = String
        override def join(xs: Seq[Char]): String = xs.mkString("+")
      }
      val s:String = doJoin('1','2') //doesn't work
      println(s)
    }

    def do2: Unit ={
      implicit val charToInt = new Joiner[Char] {
        override type R = Int
        override def join(xs: Seq[Char]): Int = xs.mkString.toInt
      }
      val n :Int = doJoin('1','2') //doesn't work
      println(n)
    }

    do1
    do2


  }

}
Ivan
  • 462
  • 3
  • 13
  • Yes. with one implicit in scope, it is fine. With two implicit candiates, it breaks down while the purely parameter type works regardless since I can specify both type. So question is how to make the mixed version work noting that I can't specify return type explicitly to help the compiler resolve the implicit. – lawal Jul 21 '17 at 07:39
0

How about this. You can specify your return type and the input type is taken from argument.

import scala.language.reflectiveCalls

trait Joiner[T, R] {
  def join(xs: Seq[T]): R
}

def doJoin[R] = new {
    def apply[T](xs: T*)(implicit j: Joiner[T, R]): R = j.join(xs)
}

implicit val charToStringJoiner = new Joiner[Char, String] {
  override def join(xs: Seq[Char]): String = xs.mkString("+")
}

implicit val charToInt = new Joiner[Char, Int] {
  override def join(xs: Seq[Char]): Int = xs.mkString.toInt
}

implicit val addJoiner = new Joiner[Int, Int] {
  override def join(xs: Seq[Int]): Int = xs.sum
}

println(doJoin[String]('1', '2'))
println(doJoin[Int]('1', '2'))
println(doJoin[Int](1, 2))
Fred4242
  • 31
  • 5