11

I am trying to define a Map literal with key: String, value: (Any)=>String. I tried the following, but get a syntax error:

def foo(x: Int): String = /...
def bar(x: Boolean): String = /...
val m = Map[String, (Any) => String]("hello" -> foo, "goodbye" -> bar)
1
  • 1
    Even if the syntax in your question worked, there must be something funny you're doing with types to send the right type to the function you get from the map. There's a lot more complication here. What are you really trying to do? Maybe there's a better solution entirely that doesn't rely on a map of functions of different types. Commented Jan 6, 2011 at 18:05

5 Answers 5

10

Funny that no one actually gave a type that would work. Here's one such:

def foo(x: Int): String = x.toString
def bar(x: Boolean): String = x.toString
val m = Map[String, (Nothing) => String]("hello" -> foo, "goodbye" -> bar)

The reason why it works this way is because Function1 is contra-variant on the input, so (Nothing) => String is a superclass of (Int) => String. It is also co-variant on the output, so (Nothing) => Any would be a superclass to any other Function1.

Of course, you can't use it like that. Without manifests, you can't even uncover what the original type of Function1 is. You could try something like this, though:

def f[T : Manifest](v: T) = v -> manifest[T]
val m = Map[String, ((Nothing) => String, Manifest[_])]("hello" -> f(foo), "goodbye" -> f(bar))

val IntManifest = manifest[Int]
val BooleanManifest = manifest[Boolean]
val StringManifest = manifest[String]
m("hello")._2.typeArguments match {
    case List(IntManifest, StringManifest) =>
        m("hello")._1.asInstanceOf[(Int) => String](5)
    case List(BooleanManifest, StringManifest) =>
        m("hello")._1.asInstanceOf[(Boolean) => String](true)
    case _ => "Unknown function type"
}
Sign up to request clarification or add additional context in comments.

Comments

4

Int => String is not a subclass of Any => String, rather, the contrary. You can't put (replace) an Int => String function when a code expects Any => String, since that code can apply the function with, say, "hi".

@Ben suggestion works, but how is it useful? you can't invoke the function once you get it from the Map.

If you really want to do this, maybe define foo as a partial function:

val foo: PartialFunction[Any, String] = {case i: Int => ....}

Obviously, this will fail at runtime if you pass it a string, but you can always test if the function is ok for use with your parameter by using isDefinedAt. (another alternative may be manifests, but I don't see the value here)

1 Comment

Yes, I am about to post the question "how do you invoke it?" because I don't know myself.
4

If I let the compiler infer it I seem to get an illegal type:

scala> val m = Map("hello" -> foo _, "goodbye" -> bar _)
m: scala.collection.immutable.Map[java.lang.String,(Boolean with Int) => String] =
                Map((hello,<function1>), (goodbye,<function1>))

scala> m("hello")(8)
<console>:9: error: type mismatch;
 found   : Int(8)
 required: Boolean with Int
       m("hello")(8)
scala> var q = new Boolean with Int
<console>:5: error: illegal inheritance from final class Boolean
       var q = new Boolean with Int

Anyway, what you want is not the type Any but a generic of "any type" which is _:

scala> val mm = Map[String, (_) => String]("hello" -> foo _, "goodbye" -> bar _)
mm: scala.collection.immutable.Map[String,Function1[_, String]] =
               Map((hello,<function1>), (goodbye,<function1>))

I just posted a question about how to invoke such functions because I don't actually know.

Comments

3

Trait Function1 is contravariant for parameter, so def foo(x: Int): String is not a (Any) => String. So the following would work:

scala> def baz(x: Any): String = "baz"                         
baz: (x: Any)String

scala> val m2 = Map[String, (String) => String]("hello" -> baz)
m2: scala.collection.immutable.Map[String,(String) => String] = Map((hello,<function1>))

Comments

0

This is how I did it to fulfill a similar requirement.

object MapToMethods {
private def increment(x: Int): Int = x+1
private def decrement(x: Int): Int = x-1

val m: Map[String, Int => Int] =Map("increment" -> increment, "decrement" ->decrement)

println(m("increment")(2)) //prints 3
println(m("decrement")(3)) //prints 2
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.