user1860041
user1860041

Reputation: 21

Scala: compare type of generic class

There have been many questions on that issue, but sadly none seems to solve my problem.

I've written a generic scala class, let's call it

class MyClass[A]() { ... }

As well as the according object:

object MyClass() { ... }

Inside MyClass I want to define a function whichs behaviour depends on the given type A. For instance, let's just assume I want to define a 'smaller' function of type (A, A) => Boolean, that by default returns 'true' no matter what the elements are, but is meant to return the correct results for certain types such as Int, Float etc.

My idea was to define 'smaller' as member of the class in the following way:

class MyClass[A]() {

    val someArray = new Array[A](1) // will be referred to later on

    var smaller:(A,A) => Boolean = MyClass.getSmallerFunction(this)

    ...some Stuff...

}

object MyClass {

    def getSmallerFunction[A](m:MyClass[A]):(A,A) => Boolean = { 

        var func = (a:Boolean, b:Boolean) => true

        // This doesn't compile, since the compiler doesn't know what 'A' is
        if(A == Int) func = ((a:Int, b:Int) => (a<b)).asInstanceOf[(A,A) => Boolean)]

        // This compiles, but always returns true (due to type erasure I guess?)
        if(m.isInstanceOf[MyClass[Float]]) func = ((a:Float, b:Float) => (a<b)).asInstanceOf[(A,A) => Boolean)]

        // This compiles but always returns true as well due to the newly created array only containing null-elements
        if(m.someArray(0).isInstanceOf[Long]) func = ((a:Long, b:Long) => (a<b)).asInstanceOf[(A,A) => Boolean)]

    }

    ...some more stuff...

}

The getSmallerFunction method contains a few of the implementations I experimented with, but none of them works.

After a while of researching the topic it at first seemed as if manifests are the way to go, but unfortunately they don't seem to work here due to the fact that object MyClass also contains some constructor calls of the class - which, no matter how I change the code - always results in the compiler getting angry about the lack of information required to use manifests. Maybe there is a manifest-based solution, but I certainly haven't found it yet.

Note: The usage of a 'smaller' function is just an example, there are several functions of this kind I want to implement. I know that for this specific case I could simply allow only those types A that are Comparable, but that's really not what I'm trying to achieve.

Sorry for the wall of text - I hope it's possible to comprehend my problem.

Thanks in advance for your answers.

Edit:

Maybe I should go a bit more into detail: What I was trying to do was the implementation of a library for image programming (mostly for my personal use). 'MyClass' is actually a class 'Pixelmap' that contains an array of "pixels" of type A as well as certain methods for pixel manipulation. Those Pixelmaps can be of any type, although I mostly use Float and Color datatypes, and sometimes Boolean for masks. One of the datatype dependent functions I need is 'blend' (although 'smaller' is used too), which interpolates between two values of type A and can for instance be used for smooth resizing of such a Pixelmap. By default, this blend function (which is of type (A,A,Float) => A) simply returns the first given value, but for Pixelmaps of type Float, Color etc. a proper interpolation is meant to be defined. So every Pixelmap-instance should get one pointer to the appropriate 'blend' function right after its creation.

Edit 2:

Seems like I found a suitable way to solve the problem, at least for my specific case. It really is more of a work around though.

I simply added an implicit parameter of type A to MyClass:

class MyClass[A]()(implicit dummy:A) { ... }

When I want to find out whether the type A of an instance m:MyClass is "Float" for instance, I can just use "m.dummy.isInstanceOf[Float]". To make this actually work I added a bunch of predefined implicit values for all datatypes I needed to the MyClass object:

object MyClass {

    implicit val floatDummy:Float = 0.0f
    implicit val intDummy:Int = 0
    ...

}

Although this really doesn't feel like a proper solution, it seems to get me around the problem pretty well.

Upvotes: 1

Views: 2416

Answers (1)

Dominic Bou-Samra
Dominic Bou-Samra

Reputation: 15416

I've omitted a whole bunch of stuff because, if I'm honest, I'm still not entirely sure what you're trying to do. But here is a solution that may help you.

trait MyClass[A] {
  def smaller: (A,A) => Boolean
}

object MyClass {
  implicit object intMyClass extends MyClass[Int] {
    def smaller = (a:Int, b:Int) => (a < b)
  }
  implicit object floatMyClass extends MyClass[Float] {
    def smaller = (a:Float, b:Float) => (a < b)
  }
  implicit object longMyClass extends MyClass[Long] {
    def smaller = (a:Long, b:Long) => (a < b)
  }

  def getSmallerFunction[T : MyClass](a: T, b: T) = implicitly[MyClass[T]].smaller(a, b)
}

The idea is that you define your smaller methods as implicit objects under your MyClass, object, with a getSmallerFunction method. This method is special in the sense that it looks for a type-class instance that satisfies it's type bounds. We can then go:

println(MyClass.getSmallerFunction(1, 2))

And it automagically knows the correct method to use. You could extend this technique to handle your Array example. This is a great tutorial/presentation on what type-classes are.

Edit: I've just realise you are wanting an actual function returned. In my case, like yours the type parameter is lost. But if at the end of the day you just want to be able to selectively call methods depending on their type, the approach I've detailed should help you.

Upvotes: 2

Related Questions