Каков наилучший способ создания и передачи словарей, содержащих несколько типов в Scala? - PullRequest
2 голосов
/ 03 августа 2011

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

Опции, о которых я знаю, включают создание case-классов, создание аноновых объектов и создание карт из Strings -> Any.

  • Классы Case требуют умственных издержек для создания (имен), но строго типизированы.
  • Объекты Anon не кажутся хорошо документированными, и мне неясно, как использовать их в качестве аргументов, поскольку именованный тип отсутствует.
  • Карты из String -> Любые, требующие приведения для поиска.

Есть что-нибудь лучше?

В идеале они могут быть построены из json и преобразованы обратно в него, когда это необходимо.

Мне не нужна статическая типизация (хотя это было бы неплохо, я вижу, как это было бы невозможно), - но я хочу избежать явного приведения.

Ответы [ 4 ]

3 голосов
/ 03 августа 2011

Вот фундаментальная проблема с тем, что вы хотите:

def get(key: String): Option[T] = ...

val r = map.get("key")

Тип r будет определен из возвращаемого типа get - так, каким должен быть этот тип?Откуда это можно определить?Если вы сделаете его параметром типа, то это будет относительно просто:

import scala.collection.mutable.{Map => MMap}
val map: MMap[String, (Manifest[_], Any) = MMap.empty
def get[T : Manifest](key: String): Option[T] = map.get(key).filter(_._1 <:< manifest[T]).map(_._2.asInstanceOf[T])
def put[T : Manifest](key: String, obj: T) = map(key) = manifest[T] -> obj

Пример:

scala> put("abc", 2)

scala> put("def", true)

scala> get[Boolean]("abc")
res2: Option[Boolean] = None

scala> get[Int]("abc")
res3: Option[Int] = Some(2)

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

Любое решение, которое вы примете, приведет к той же самой проблеме: так или иначе,вам нужно будет указать компилятору, какой тип должен быть возвращен.

Теперь это не должно быть бременем в программе Scala.Возьмите r выше ... затем вы будете использовать r для чего-то, верно?То, для чего вы его используете, будет иметь методы, соответствующие какому-то типу, и, поскольку вы знаете, что это за методы, вы должны также знать, каким должен быть тип r.

Если это не такв этом случае что-то в корне не так с кодом - или, возможно, вы не захотели получить карту, чтобы знать, что вы будете с ней делать.

2 голосов
/ 03 августа 2011

Вы можете использовать подход, который я видел в библиотеке casbah , когда вы явно передаете параметр типа в метод get и приводите фактическое значение в методе get.Вот краткий пример:

<code>case class MultiTypeDictionary(m: Map[String, Any]) {
  def getAs[T <: Any](k: String)(implicit mf: Manifest[T]): T =
    cast(m.get(k).getOrElse {throw new IllegalArgumentException})(mf)<br/>
  private def cast[T <: Any : Manifest](a: Any): T =
    a.asInstanceOf[T]
}<br/>
implicit def map2multiTypeDictionary(m: Map[String, Any]) =
    MultiTypeDictionary(m)<br/>
val dict: MultiTypeDictionary = Map("1" -> 1, "2" -> 2.0, "3" -> "3")<br/>
val a: Int = dict.getAs("1")
val b: Int = dict.getAs("2") //ClassCastException
val b: Int = dict.getAs("4") //IllegalArgumetExcepton

Обратите внимание, что реальных проверок во время компиляции не существует, поэтому вам приходится иметь дело со всеми исключениями.

UPD Рабочий класс MultiTypeDictionary

2 голосов
/ 03 августа 2011

То есть вы хотите проанализировать json и превратить его в объекты, которые напоминают объекты javascript, описанные во входных данных json? Если вы хотите статическую типизацию, классы падежей являются практически единственным вариантом, и уже есть библиотеки, обрабатывающие это, например, lift-json.

Другой вариант - использовать экспериментальную поддержку Scala 2.9 для динамической типизации. Это даст вам элегантный синтаксис за счет безопасности типов.

1 голос
/ 03 августа 2011

Если у вас есть только ограниченное количество типов, которые могут встречаться в качестве значений, вы можете использовать какой-либо тип объединения (иначе говоря, непересекающийся тип), например, с Map[Foo, Bar | Baz | Buz | Blargh].Если у вас есть только две возможности, вы можете использовать Either[A,B], что дает вам Map[Foo, Either[Bar, Baz]].Для трех типов вы можете обмануть и использовать Map[Foo, Either[Bar, Either[Baz,Buz]]], но этот синтаксис явно плохо масштабируется.Если у вас есть больше типов, вы можете использовать такие вещи, как ...

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