views:

340

answers:

4

Hi scala gurus

Can anyone re-write this code to do the same thing but without any compiler warnings please:-

object TestTypeErasure {

  def main(args:Array[String]) {

    def myFunction(myObject:Any):Boolean = {
      true
    }

    val myVariable: (Any => Boolean) = myFunction

    myVariable match {
      case function:(Any => Boolean) => println("Match")
    }

  }
}

Thousand thankyous

Keith

Update!!!!. Sorry making a real hash of this. Its my first question on SO

Just to let the gurus know I have tried something along the lines of this also to no avail:- (Can't compile it)

object TestTypeErasure {  

    def doTest(parameter: Any) = {  
        parameter match {  
            case function:Function1[_, _] => function("Whatever")  
        }  
    }  

    def main(args:Array[String]) {  
    }  

}  

I get error :-

TestTypeErasure.scala:6: error: type mismatch;
  found   : java.lang.String("Whatever")
  required: _
    case function:Function1[_, _] => function("Whatever")
                                                ^
one error found

Thanks again

Keith

A: 

Have a look at this question: http://stackoverflow.com/questions/1534702/pattern-matching-zero-argument-functions-in-scala-mystified-by-warning

Emil Ivanov
Hi I had taken a look at that. I tried the top answer. The next difficulty is that I want to call the function.If I change the code to actually call the function like below I get a compiler error. Does anybody know how I can get around this object TestTypeErasure { def doTest(parameter: Any) = { parameter match { case function:Function1[_, _] => function("Whatever") } } def main(args:Array[String]) { } }Thanks againKeith
Keith
A: 

My last response didn't come out too good on the comment so I will add it again as an answer. (Sorry, I'm new here!!)

I had taken a look at that. I tried the top answer. The next difficulty is that I want to call the function.

If I change the code to actually call the function like below I get a compiler error. Does anybody know how I can get around this

object TestTypeErasure { 

    def doTest(parameter: Any) = { 
        parameter match { 
            case function:Function1[_, _] => function("Whatever") 
        } 
    } 

    def main(args:Array[String]) { 
    } 

} 

Thanks again

Keith

Keith
It's probably better to edit the question and provide further info, so all your requirements are in one place.
Brian Agnew
+3  A: 

You can capture the type information with Manifests. (T, R are invariant here to keep things simple.)

import scala.reflect._
def matchFunction[T,R](f: Function1[T,R], t : T)(implicit mt : Manifest[T], mr : Manifest[R]) = {
  val result = if ((mt.erasure, mr.erasure) == (classOf[Any], classOf[Boolean]))  "any, boolean " + f(t)
  else if((mt.erasure, mr.erasure) == (classOf[Int], classOf[Int])) "int, int " + f(t)
  else "Unknown " + f(t)
  println(result)
}

scala>     matchFunction((x : Int) => x + 1, 1)
int, int 2

scala>     matchFunction((x : Any) => true, 1 : Any)
any, boolean true

scala>     matchFunction((x : Boolean) => ! x, false)
Unknown 

For Scala 2.8 one can use context bounds, removing the two implicit parameters:

import scala.reflect._
def matchFunction[T: Manifest,R : Manifest](f: Function1[T,R], t : T) = {
  val mt = implicitly[Manifest[T]]
  val mr = implicitly[Manifest[T]]
  val result = if ((mt.erasure, mr.erasure) == (classOf[Any], classOf[Boolean]))  "any, boolean " + f(t)
  else if((mt.erasure, mr.erasure) == (classOf[Int], classOf[Int])) "int, int " + f(t)
  else "Unknown " + f(t)
  println(result)
}
Thomas Jung
Just note that you need Scala 2.8 for this.
retronym
@retronym No, this works in 2.7.7. Manifest ought to be available since 2.7.2.
Thomas Jung
A: 

There are a variety of approaches that might work, none of which are all that straightforward.

One option is to use manifests, but you'd have to define your own variant of match that was manifest-aware. You can read more about manifests here. If you have to do this sort of thing a lot, that would be the way to go, although this feature is still considered to be experimental.

Another option, if your usage is relatively lightweight, is to wrap the function in some class that is not generic. For example,

object Example {
  def hasString(a:Any) = (a!=null && a.toString.length>0)

  case class AnyImpliesBoolean(f: (Any) => Boolean) { } 
  implicit def callAIB(aib: AnyImpliesBoolean) = aib.f

  def callWrapped(a: Any) {
    a match {
      case aib: AnyImpliesBoolean => println( aib("Check") )
      case _ => println("(Nothing)")
    }
  }

  def tryMe() {
    val has = AnyImpliesBoolean(hasString _)
    callWrapped( has )
    callWrapped("foo")
    callWrapped((s:String)=>true)
  }
}

scala> Example.tryMe
true
(Nothing)
(Nothing)

If you are wrapping several different functions, but not too many of them, you can create a base class WrappedFunction and then have things like AnyImpliesBoolean extend WrappedFunction.

Yet another option is to not actually pass functions around, but to instead use reflection to pass java.lang.Methods around. Methods know their types. Even with some pretty Scala wrappers, it still would be a little clunky (and it's not high-performance).

(Edited to add the manifest link I was missing.)

Rex Kerr