Определение карты из строки в функцию в Scala
Я пытаюсь определить литерал Map с ключом: String
, value: (Any)=>String
. Я пробовал следующее, но получаю синтаксическую ошибку:
def foo(x: Int): String = /...
def bar(x: Boolean): String = /...
val m = Map[String, (Any) => String]("hello" -> foo, "goodbye" -> bar)
Ответы
Ответ 1
Забавно, что никто не дал тип, который бы сработал. Здесь один такой:
def foo(x: Int): String = x.toString
def bar(x: Boolean): String = x.toString
val m = Map[String, (Nothing) => String]("hello" -> foo, "goodbye" -> bar)
Причина, по которой он работает, заключается в том, что Function1
является противоречивым на входе, поэтому (Nothing) => String
является суперклассом (Int) => String
. Он также является ко-вариантом на выходе, поэтому (Nothing) => Any
будет суперклассом для любого другого Function1
.
Конечно, вы не можете так использовать. Без манифестаций вы даже не можете узнать, что такое оригинальный тип Function1
. Вы могли бы попробовать что-то вроде этого:
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"
}
Ответ 2
Если я позволю компилятору сделать вывод, что это похоже на незаконный тип:
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
В любом случае, вы хотите не тип Any
, а общий тип "любого типа", который _
:
scala> val mm = Map[String, (_) => String]("hello" -> foo _, "goodbye" -> bar _)
mm: scala.collection.immutable.Map[String,Function1[_, String]] =
Map((hello,<function1>), (goodbye,<function1>))
Я только что разместил вопрос о как вызвать такие функции, потому что я действительно не знаю.
Ответ 3
Int = > String не является подклассом Any = > String, скорее наоборот. Вы не можете поместить (заменить) функцию Int = > String, когда код ожидает Any = > String, поскольку этот код может применять функцию с, скажем, "привет".
@Ben предложение работает, но как оно полезно? вы не можете вызвать функцию, как только вы ее получите с карты.
Если вы действительно этого хотите, можете определить foo как частичную функцию:
val foo: PartialFunction[Any, String] = {case i: Int => ....}
Очевидно, что это провалится во время выполнения, если вы передадите ему строку, но вы всегда можете проверить, подходит ли функция для использования с вашим параметром с помощью isDefinedAt. (другая альтернатива может проявляться, но я не вижу здесь значения)
Ответ 4
Trait Function1
контравариантен для параметра, поэтому def foo(x: Int): String
не является (Any) => String
. Так будет работать следующее:
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>))