Карта различных типов функций в Scala - PullRequest
2 голосов
/ 21 февраля 2012

У меня есть две функции с разными сигнатурами типов, и я хочу сохранить их обе на карте:

val add = ((a: Int, b: Int) => a + b)
val odd = ((a: Int) => a % 2 == 0)

var funcs = Map[String, Any]("add" -> add, "odd" -> odd)

Затем я хочу получить доступ к этим функциям позднее, без необходимости знать об ихтипы.Как бы я это сделал?Я могу написать:

funcs("add").asInstanceOf[(Int, Int) => Int](1, 2)

Но это требует от меня либо

  1. знать заранее, какой тип функции
  2. сделать какой-то шаблонсопоставить с этим каждый возможный тип .

Есть ли какой-нибудь способ, которым я могу узнать тип объекта, хранящегося как Any, и преобразовать его в этот тип?

Ответы [ 3 ]

6 голосов
/ 21 февраля 2012

Хотя это, конечно, не очень хорошая идея, если вам действительно придется сделать это, вы можете сделать это с помощью отражения.

Используя утилиту PaulP's Invocation :

scala> val add: (Int, Int) => Int = _ + _
add: (Int, Int) => Int = <function2>

scala> val isOdd: Int => Boolean = _ % 2 != 0
isOdd: Int => Boolean = <function1>

scala> import Invocation._
import Invocation._

scala> val funcs = Map("add" -> add, "isOdd" -> isOdd)
funcs: scala.collection.immutable.Map[java.lang.String,ScalaObject] = Map(add -> <function2>, isOdd -> <function1>)

scala> funcs("add") o 'apply(3, 4)
res18: Any = 7

scala> funcs("isOdd") o 'apply(11)
res19: Any = true
4 голосов
/ 21 февраля 2012

Есть ли какой-нибудь способ, которым я могу узнать тип объекта, хранящегося как Any, и преобразовать его в этот тип?

Это на самом деле не имеет смысла.Предположим, я мог бы сделать это, и это выглядит так:

val func = funcs(func_name).toAppropriateType

Что теперь?

func теперь содержит значение некоторого неизвестного типа.Я могу назвать это ... но я не знаю его подписи, так какие аргументы я передаю?И тип / подпись будет отличаться при каждом вызове (в зависимости от того, что было извлечено из карты), поэтому код для его вызова должен быть разным для каждого вызова;Мне нужен какой-то способ выполнения разного кода для каждой возможности func ... но это будет совпадение с шаблоном!И он должен был бы учитывать каждый возможный тип, точно так же как совпадение на Any.

На самом деле, магический метод toAppropriateType, даже если он мог существовать, не дает мне ничего поверх просто с помощью Any.Это все еще тот случай, когда единственное, что я могу сделать с func, это то, что вы можете делать со всеми значениями каждого возможного типа (т. Е. Очень мало).

В общем, когда вычтобы взять что-то из коллекции, вы должны обработать это кодом, действительным для всего, что позволяет тип помещать в коллекцию .Единственная альтернатива - использовать такие функции, как asInstanceOf, который отбрасывает гарантии системы типов и возлагает ответственность за то, чтобы вы не использовали неправильный тип;Это означает, что у вас должен быть какой-то другой способ узнать, какой тип ожидать вне системы типов.

Если вы хотите иметь набор функций с одним из заранее определенных наборов сигнатур, тогда лучший способзаключается в создании типа, который допускает только эти сигнатуры и позволяет определить разницу между ними (например, предложение Рекса Керра об использовании Either).Переход на использование Any, как только вы обнаружите, что вам нужно объединить вещи разных типов в коллекции, обычно вызывает больше проблем, чем решает.

2 голосов
/ 21 февраля 2012

Вы можете хранить их в Either:

val funcs = Map[String, Either[Int=>Boolean, (Int,Int)=>Int]](
  "add" -> Right(add),
  "odd" -> Left(odd)
)

Теперь вы можете снова разделить их, хотя сопоставление с образцом здесь привередливое (Right(f) будет работать, но не пытайтесь ставить условия на то, что есть f). Я предлагаю использовать fold, right, leftisRight и isLeft, если вам действительно нужно), например, так:

def doSomething(s: String) =
  funcs(s).fold(
    f => { f(4) },      // Left is Int=>Boolean
    g => { g(3,5)==8 }  // Right is (Int,Int)=>Int
  )

Предположительно, вам нужен какой-то способ загрузки соответствующих аргументов вместо фиктивных значений, которые я ввел.

Если у вас много вариантов функций, вы можете определить свою собственную иерархию, похожую на другую, за исключением более разных случаев, или вы можете вложить Either s.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...