Определение карты из строки в функцию в 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>))