2015-09-28 6 views
1

У меня есть общая карта со значениями, некоторые из которых могут быть в свою очередь списками значений. Я пытаюсь обработать заданный ключ и преобразовать результаты к типу ожидаемого внешнего вызывающего абонента, например:Специализация методов Scala для определенных тегов

// A map with some values being other collections. 
val map: Map[String, Any] = Map("foo" -> 1, "bar" -> Seq('a', 'b'. 'a')) 

// A generic method with a "specialization" for collections (pseudocode) 
def cast[T](key: String) = map.get(key).map(_.asInstanceOf[T]) 
def cast[C <: Iterable[T]](key: String) = map.get(key).map(list => list.to[C].map(_.asIntanceOf[T])) 

// Expected usage 
cast[Int]("foo")   // Should return 1:Int 
cast[Set[Char]]("bar") // Should return Set[Char]('a', 'b') 

Это, чтобы показать, что я хотел бы сделать, но это не работает. Ошибка компилятора (правильно, около 2 возможных совпадений). Я также попытался сделать это единственной функцией с каким-то совпадением шаблонов в типе, но безрезультатно.

Я читал на @specialized, TypeTag, CanBuildFrom и других функциях scala, но мне не удалось найти простой способ собрать все это вместе. Отдельные примеры Я нашел адреса разных частей и некоторые уродливые обходные пути, но ничего, что просто позволило бы внешнему пользователю вызвать cast и получить исключение, это отличное отключение. Некоторые вещи тоже старые, я использую Scala 2.10.5.

+1

Зачем вам нужен второй способ произнесения (для итерацию)? Первого должно быть достаточно. – roterl

+0

Я думаю, что я не вижу проблемы. 'def cast (m: Map [String, Any], key: String) = m (key) match {case x: Int => x}' правильно возвращает целое число и бросает с набором. Для определения типа набора может потребоваться classtags, но он работает с вашим примером – Archeg

+0

, который изменяет заголовок для 'cast', и я не хочу, чтобы он кидал с помощью набора, я хочу, чтобы набор был корректным. Кроме того, вызов 'cast [Set [Char]]' автоматически не вызывает вторую перегрузку, даже более конкретную, чем первая. –

ответ

1

Это похоже на работу, но у него есть некоторые проблемы.

def cast[T](m: Map[String, Any], k: String):T = m(k) match { 
    case x: T => x 
} 

С правильным вводом вы получаете правильный выход.

scala> cast[Int](map,"foo") 
res18: Int = 1 

scala> cast[Set[Char]](map,"bar") 
res19: Set[Char] = Set(a, b) 

Но он бросает, если тип является неправильным для ключа или если карта не имеет такой ключ (конечно).

+0

Спасибо за ответ. Это действительно то, что я делаю в качестве альтернативы, но оно соответствует типу объекта, полученному, а не типу вызова. Таким образом, для меня невозможно выполнить пользовательское преобразование, например, Seq to Set или другое. Я хочу иметь возможность запускать собственный код в зависимости от типа, применяемого к методу (следовательно, специализации). –

1

Вы можете сделать это с помощью скрытых параметров:

val map: Map[String, Any] = Map("foo" -> 1, "bar" -> Set('a', 'b')) 

abstract class Casts[B] {def cast(a: Any): B} 
implicit val doubleCast = new Casts[Double] { 
    override def cast(a: Any): Double = a match { 
    case x: Int => x.toDouble 
    } 
} 

implicit val intCast = new Casts[Int] { 
    override def cast(a: Any): Int = a match { 
    case x: Int => x 
    case x: Double => x.toInt 
    } 
} 

implicit val seqCharCast = new Casts[Seq[Char]] { 
    override def cast(a: Any): Seq[Char] = a match { 
    case x: Set[Char] => x.toSeq 
    case x: Seq[Char] => x 
    } 
} 


def cast[T](key: String)(implicit p:Casts[T]) = p.cast(map(key)) 

println(cast[Double]("foo")) // <- 1.0 
println(cast[Int]("foo"))  // <- 1 
println(cast[Seq[Char]]("bar")) // <- ArrayBuffer(a, b) which is Seq(a, b) 

Но вам все равно нужно перебрать все тип-к-типа параметров, что является разумным, как Set('a', 'b').asInstanceOf[Seq[Char]] броски, и вы не можете использовать универсальный оттенок, так вам нужно обращаться с такими случаями по-разному.

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

+0

Да. Мне нужно было бы добавить один неявный для каждого типа :-( –