Reputation: 1097
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.
Upvotes: 0
Views: 1073
Reputation: 1097
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"))
Upvotes: 0
Reputation: 7162
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.
Upvotes: 1