views:

73

answers:

1

I am writing an interpreter and tried to use solution from how-to-set-up-implicit-conversion-to-allow-arithmetic-between-numeric-types for the same problem I need to be able to add Boolean + Boolean, Int + Boolean, Boolean + Int, Int + Double, Double + Double etc.

So I used WeakConformance and C classes from that solution

sealed trait WeakConformance[A <: AnyVal, B <: AnyVal, C] {
  implicit def aToC(a: A): C

  implicit def bToC(b: B): C
}

object WeakConformance {
  implicit def SameSame[T <: AnyVal]: WeakConformance[T, T, T] = new WeakConformance[T, T, T] {
    implicit def aToC(a: T): T = a

    implicit def bToC(b: T): T = b
  }

  implicit def IntDouble: WeakConformance[Int, Double, Double] = new WeakConformance[Int, Double, Double] {
    implicit def aToC(a: Int) = a

    implicit def bToC(b: Double) = b
  }

  implicit def DoubleInt: WeakConformance[Double, Int, Double] = new WeakConformance[Double, Int, Double] {
    implicit def aToC(a: Double) = a

        implicit def bToC(b: Int) = b
      }
   }  

   case class C[A <: AnyVal](val value:A) {
          import WeakConformance.unify
          def +[B <: AnyVal, WeakLub <: AnyVal](that:C[B])(implicit wc: WeakConformance[A, B, WeakLub], num: Numeric[WeakLub]): C[WeakLub] = {  
        new C[WeakLub](num.plus(wc.aToC(x), wc.bToC(y)))
      }
    }

and here is part of my interpreter

class Interpreter {

......

  def eval(e: Expression): Any = e match {
  ...

    case ADD(lhs, rhs) => (eval(lhs), eval(rhs)) match {

      case (l: C[_], r: C[_]) => l + r  // error comes here

      case _ => error("...")
    }
  }

}

the error is like that

error: ambiguous implicit values: // shows 2 last objects declared as implicit in Numeric trait here match expected type Numeric[WeakLub]

any ideas how to make it work? I wanted to make the eval method to return C but since C[Int] is not an instance of C[Any] it doesn't solve my problem

+1  A: 

Because of type erasure, you can't retrieve at run-time the type parameter of C. You'll need to use manifests to store that information. See questions related to manifest and type erasure.

Daniel
Yes, I know about Manifests. I was just hoping you would suggest a way to do this. Does this mean that my eval method should return a pair C with a manifest instead of just C?
Tala
I mean your _C_ should keep a manifest. Try declaring it like this: `case class C[A <: AnyVal](val value:A)(implicit man: Manifest[A])`, and using these manifests to cast them into the proper types.
Daniel
I changed my C class as you advised, but still the problem exists. Can you tell me how I need to modify the interpreter code shown above?
Tala
Daniel
:-) That's exactly the thing I was trying to avoid. I could simply make subclasses of C like 'intC extends C[Int]' and just case through all the possible combinations that is much cheaper than dealing with manifests. But since there are lots of types and lots of operations this will cause a huge code duplication. There must be a better way.
Tala
@Tala You must avoid type parameters to get it working like this, because they are erased at run-time. If you need type at run-time, you'd better go the way of subclassing. Perhaps redesign your solution.
Daniel
Hi, Daniel. I am still looking for a solution (the last one will be a lot of code dublication) Can I create a Map of types and name of the function to corresponding converting functions like '(Int, Double, SUM) -> function that converts them into one type and adds' etc.? Then I would have to check the types of left and right operands and pass a function that does a job. is it possible and worth doing?
Tala