0
votes

I am experementing with scala type classes and came upon following problem. I created a class with multiple methods. Each methods has an implicit parameter B and returns an Either[A,B].

Type B is a converter where the caller can provide a custom object to wrap the response the way he needs it.

So here is the code

  case class A( var value:String){

  }

  case class Converter[A]( value : (Map[String, String] ) => A )

  object Converter{
     implicit val AConverter = new Converter[A]( (x:Map[String, String]) => new A("Hello World") )
     implicit val IntConverter = new Converter[Int]( (x:Map[String, String]) => 10 )
  }

  class API{

    def method1[B : Converter] : Either[A, B] = {
      Right( implicitly[Converter[B]].value(Map.empty))
    }

    def method2[B : Converter](name:String) : Either[A, B] = {
      Right( implicitly[Converter[B]].value(Map.empty))
    }

    def method3[B : Converter](id:Int) : Either[A, B] = {
      Right( implicitly[Converter[B]].value(Map.empty))
    }

  }

Now here is my trouble. I want to archieve following

Each method should be callable without worring about the implicit param B because there should be a default implementation somewhere be defined.

So i defined some implicits in the companion object but that does not solve my problem at all!

  // Works due implicit definiton
  println( new API().method1[A])
  // Works due implicit definiton
  println( new API().method1[Int])
  // Works not but should
  println( new API().method1)

I want to say the type of B for method1 is Converter[A] and the type of B for method2 is Converter[Int] if the caller does not provide it.

  // Implementation should use my default Converter[A]
  println( new API().method1)

I came across this question here

https://stackoverflow.com/a/29205892/452265

So how can i provide a default type if the type itself is not passed to the method at all like in the example. I need this per method not per class.

2
So you want to have a default type parameter? This has already been asked today and the answers are already on SO.M.K.
This is the same link i posted but that does not solve my problem :)Jay
Why doesn't it solve your problem? You want a default type to be provided in case one isn't provided? Correct? Whether it is a class or method doesn't matter. You can have type parameterisation on a class level or method level.M.K.
Because in that scenario one tries to init a local var p:T where i want to say if the implicit is not given type B should be of Converter[A]. Look at the code its different!Jay

2 Answers

1
votes

Well this might not be exactly an answer to the question, but rather a solution proposal for your problem.

If you want to call new API().method1 then, instead of looking for a way to specify default type parameters, why not define a function method1 without type parameter?

case class A( var value:String){

}

case class Converter[A]( value : (Map[String, String] ) => A )

object Converter{
  implicit val AConverter = new Converter[A]( (x:Map[String, String]) => new A("Hello World") )
  implicit val IntConverter = new Converter[Int]( (x:Map[String, String]) => 10 )
}

class API{

  def method1: Either[A, A] = {
    Right(Converter.AConverter.value(Map.empty))
  }

  def method1[B : Converter] : Either[A, B] = {
    Right( implicitly[Converter[B]].value(Map.empty))
  }

  def method2[B : Converter](name:String) : Either[A, B] = {
    Right( implicitly[Converter[B]].value(Map.empty))
  }

  def method3[B : Converter](id:Int) : Either[A, B] = {
    Right( implicitly[Converter[B]].value(Map.empty))
  }

}

val undefined = new API().method1
val defined = new API().method1[Int]

This should make it possible to call new API().method1. However, there are limitations. E.g. type inference might not work as one might expect:

val defined: Either[A, Int] = new API().method1

does not compile.

0
votes

I thought about the same like you and finally came across this solution. Its a bit of overhead but it seems to work the way i need it!

class A(  val value:String){
     val success = true;
  }

  class B(  value:String) extends A(value){
     override val success = false;
  }

  class C(  override val value:String = "Doo") extends A(value){
     override val success = false;
  }

  case class Converter[A]( value : (Map[String, String] ) => A ){

  }

  object Converter{
     implicit lazy val method1Converter  = new Converter[B]( (x:Map[String, String]) => new B("foo") )
     implicit lazy val method2Converter  = new Converter[C]( (x:Map[String, String]) => new C() )
  }

  trait method1Impl{

    def method1 : Either[A, B] = {
      Right( Converter.method1Converter.value( Map.empty ) )
    }

    def method1[B : Converter] : Either[A, B] = {
      Right( implicitly[Converter[B]].value(Map.empty))
    }
  }

  trait method2Impl{

    def method2 : Either[A, C] = {
      Right( Converter.method2Converter.value( Map.empty ) )
    }

    def method2[B : Converter]( name:String) : Either[A, B] = {
      Right( implicitly[Converter[B]].value(Map.empty))
    }
  }

  class API extends method1Impl with  method2Impl {



  }


  // Works not but should
  println( new API().method1)

  println( new API().method2[C]("Hello"))