flatMap на карте с параметром подстановочного знака - PullRequest
4 голосов
/ 10 февраля 2012

Я пытаюсь написать что-то вроде этого:

trait Typed[T]

trait Test {

  def testMap: Map[Typed[_], Int]

  def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}
}

Но я получаю следующую ошибку:

error: no type parameters for method flatMap: (f: ((Typed[_], Int)) => Traversable[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Map[com.quarta.service.querybuilder.Typed[_],Int],B,That])That exist so that it can be applied to arguments (((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 })
--- because ---
argument expression's type is not compatible with formal parameter type;
found   : ((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 }
required: ((Typed[_], Int)) => Traversable[?B]
def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}

Этот код работает, если изменить тип testMap на:

def testMap: Map[Typed[Any], Int]

В чем разница и как я могу решить мою проблему?

Ответы [ 3 ]

5 голосов
/ 11 февраля 2012

Если я правильно понял ваш вопрос, ответ таков: вы можете сделать это, если Typed является ковариантным в T, т.е. trait Typed[+T].

Пример

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Typed[+T: Manifest] {
  override def toString = "Typed[" + implicitly[Manifest[T]].toString + "]"
}

trait Test {
  def testMap: Map[Typed[_], Int]

  def foo = testMap flatMap { case (t, s) => Seq.fill(s)(t) }
}

val bar = new Test { 
  def testMap = Map(new Typed[Double]() -> 3, new Typed[Int]() -> 5)
}

// Hit Ctrl-D

scala> bar.foo
res0: scala.collection.immutable.Iterable[Seq[Typed[Any]]] = List(Typed[Double], Typed[Double], Typed[Double], Typed[Int], Typed[Int], Typed[Int], Typed[Int], Typed[Int])

Обратите внимание, что я сделал Typed класс в этом примере, чтобы получить лучший вывод. Вы можете, конечно, придерживаться trait.

Теперь, зачем здесь нужна ковариация?

Ковариация в основном означает, что если A <: B, то X[A] <: X[B]. Таким образом, если вы объявили testMap как Map[Typed[Any], Int], в то время как Typed были инвариант , вам не разрешили перейти, например, Typed[Double] для Typed[Any], хотя Double <: Any. Здесь компилятор scala, по-видимому, заменяет _ на Any в ковариантном случае (подробности см. В комментарии к расширению).

Для объяснения проблемы, связанной с подчеркиванием, я бы обратился к ответу Луиджи.

4 голосов
/ 11 февраля 2012

Мне кажется, проблема в том, что вы пытаетесь сопоставить шаблон с анонимной функцией с параметром экзистенциального типа.

Из спецификации языка, раздел 8.5, касающейся сопоставления с шаблоном анонимных функций:

Ожидаемый тип такого выражения должен быть частично определен.Это должно быть либо scala.Functionk[S1, ... , Sk, R] для некоторых k > 0, либо scala.PartialFunction[S1, R], , где аргумент type(s) S1, ... , Sk должен быть полностью определен , но тип результата R может быть неопределенным.

testMap - это экзистенциальный тип (см. Спецификацию языка 3.2.10).Экзистенциальный тип имеет форму T forSome {Q}, где Q - последовательность объявлений типов.Вы использовали специальный синтаксис-заполнитель, поэтому тип Map[Typed[_], Int] эквивалентен Map[Typed[t] forSome { type t }, Int], что может сделать сообщение об ошибке более понятным.

Что касается решения, я думаю, это зависит именно от того, чтоты пытаешься сделать, а ты не говоришь ...:)

0 голосов
/ 11 февраля 2012

Разве это не опция для параметризации Test?

trait Test [A] {
   def testMap: Map [Typed [A], Int]
   def test = testMap.flatMap {case (typed, size) => Seq.fill (size)(typed)}
}

Нужно ли объявлять Test, прежде чем вы узнаете, какой будет A?

...