2011-01-06 13 views
10

Próbuję zdefiniować literał mapy za pomocą klucza: String, wartość: (Any)=>String. Próbowałem następujących, ale pojawia się błąd składni:Definiowanie mapy od łańcucha do funkcji w Scala

def foo(x: Int): String = /... 
def bar(x: Boolean): String = /... 
val m = Map[String, (Any) => String]("hello" -> foo, "goodbye" -> bar) 
+1

Nawet jeśli składnia w twoim pytaniu zadziałała, musi być coś śmiesznego, co robisz z typami, aby wysłać odpowiedni typ do funkcji, którą dostajesz z mapy. Tutaj jest o wiele więcej komplikacji. Co tak naprawdę naprawdę robisz? Być może istnieje całkowicie lepsze rozwiązanie, które nie opiera się na mapie funkcji różnych typów. –

Odpowiedz

9

Zabawne, że nikt nie dał takiego typu, że będzie działał pod. Oto jeden taki:

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

Powodem dlaczego to działa w ten sposób dlatego, Function1 jest przeciwwskazane wariant na wejściu, więc (Nothing) => String jest nadklasą (Int) => String. Jest także współ-wariantem na wyjściu, więc (Nothing) => Any będzie nadklasą dla każdego innego Function1.

Oczywiście, nie możesz tego użyć. Bez manifestów nie można nawet odkryć, jaki jest oryginalny typ Function1. Możesz spróbować czegoś takiego:

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" 
} 
4

Jeśli pozwolę kompilator wywnioskować to wydaje mi się dostać nielegalne typ:

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 

W każdym razie, co chcesz nie jest typem Any ale ogólny od „wszelkiego rodzaju”, która jest _:

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

właśnie napisali pytanie o how to invoke such functions bo właściwie nie wiem.

3

Trait Function1 jest sprzeczny z parametrem, więc def foo(x: Int): String nie jest (Any) => String. Tak więc działałoby:

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>)) 
4

Int => String nie jest podklasą Dowolnego => ciągu, a wręcz przeciwnie. Nie można wstawić (zamienić) funkcji Int => String, gdy kod oczekuje Any => String, ponieważ ten kod może zastosować tę funkcję za pomocą, powiedzmy, "hi".

@Ben sugestia działa, ale jak to jest przydatne? nie możesz wywołać tej funkcji, gdy otrzymasz ją z mapy.

Jeśli naprawdę chcesz to zrobić, może zdefiniować foo jako częściowy funkcję:

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

Oczywiście, to nie na starcie, jeśli przejdzie ona ciąg, ale zawsze można sprawdzić, czy funkcja jest ok do użytku z twoim parametrem za pomocą isDefinedAt. (inna alternatywa może być manifestem, ale nie widzę tutaj wartości).

+0

Tak, mam zamiar opublikować pytanie "jak go wywołać?" ponieważ sam siebie nie znam. –

+0

Ups, nie zdefiniowałem foo w prawo. Naprawiony – IttayD

Powiązane problemy