Допустим, у меня есть проиндексированная коллекция (здесь не имеет значения List
или Map
):
val zipped = List(1 -> "A", 3 -> "C", 8 -> "D")
С такими сложно работать (поскольку каждая операция, например map
, должна иметь дело с index
), поэтому я хочу передать в бизнес-обработчик следующее:
case class Indexed[T](seq: Seq[T], i: Seq[Int])
val unzipped = Indexed(List("A", "C", "D"), List(1,3,8))
handler(unzipped.seq)
Но мне нужно, чтобы пользователь был ограничен, чтобы делать только map
, filter
, collect
, contains
, forall
, scanLeft
и так далее. Но не flatMap
(кроме filter
подобных), sort
, ++
и так далее. Так что любая биекция/сюръекция, но не инжекционная. В крайнем случае, пользователь может жить без filter
/flatMap
, поэтому наличие Functor
, но не Monad
может меня устроить - в любом случае приемлемое List[Option[T]] => List[T]
из моих первоначальных требований не является полным Monad
(M[M[T]] => M[T]
, T => M[T]
).
toList
, toSet
приемлемы, но я также хочу быть уверен, что возвращаемая (из бизнес-обработчика) коллекция основана на исходной. Я могу сделать это, добавив зависящий от пути Tag
(тип, зависящий от пути) к сигнатуре типа исходной коллекции и требующий коллекции с одинаковыми тегами в качестве возвращаемого типа (что можно обмануть только с asInstanceOf
). Мои первые требования могут быть удовлетворены путем реализации моего собственного Traversable
.
Итак, мое собственное «колесо» для решения этой проблемы - это просто обертка (с разрешенным только подмножеством операций + теги для обеспечения того, чтобы коллекция была одинаковой):
trait NonReorderedListT {
trait Tag
}
trait NonReorderedList[Tg <: NonReorderedListT#Tag, T] {
type Tag = Tg
def map[U](f: T => U): NonReorderedList[Tag, U] //same tag
//... other permitted operations should be here
}
object NonReorderedList {
class NonReorderedListImpl[Tg <: NonReorderedListT#Tag, T] private[NonReorderedList] (l: List[T]) extends NonReorderedList[Tg, T] {
def map[U](f: T => U) = new NonReorderedListImpl[Tag, U](l.map(f))
//... other permitted operations should be here
}
def apply[T](l: List[T]) = {
val tagC = new NonReorderedListT {} //container
new NonReorderedListImpl[tagC.Tag, T](l)
}
}
Вот результаты Scala REPL:
defined trait NonReorderedListT
defined trait NonReorderedList
defined class NonReorderedListImpl
defined object NonReorderedList
scala> val l = NonReorderedList(List(1,2,3))
warning: there was one feature warning; re-run with -feature for details
l: NonReorderedListImpl[tagC.Tag,Int] forSome { val tagC: NonReorderedListT } = NonReorderedListImpl@3620eab
scala> val res: NonReorderedList[l.Tag, Int] = l.map(_ + 1)
res: NonReorderedList[l.Tag,Int] = NonReorderedListImpl@34bddf43
scala> val m = NonReorderedList(List(1,2,3))
warning: there was one feature warning; re-run with -feature for details
m: NonReorderedListImpl[tagC.Tag,Int] forSome { val tagC: NonReorderedListT } = NonReorderedListImpl@2d8c729f
scala> val res: NonReorderedList[l.Tag, Int] = m.map(_ + 1)
<console>:31: error: type mismatch;
found : NonReorderedListImpl[m.Tag,Int]
(which expands to) NonReorderedListImpl[tagC.Tag,Int]
required: NonReorderedList[l.Tag,Int]
(which expands to) NonReorderedList[tagC.Tag,Int]
val res: NonReorderedList[l.Tag, Int] = m.map(_ + 1)
^
Тем не менее, это не должно быть такой уж редкой ситуацией, когда вам нужна такая коллекция, поэтому, возможно, у Scalaz уже есть какая-то реализация, что-то вроде NonEmptylist
, но NonReorderedList
.
Все это я делаю потому, что у меня есть одна уже заказанная коллекция (1 -> "А", 2 - "Б", 3 -> "В", 4 -> "Д", 5 -> "Е") в качестве ввод, который разбивается на (1 -> "A", 3 -> "C", 4 -> "D") и (2 -> "B", 5 -> "E"), они обрабатываются отдельно и затем слил обратно (первоначальный порядок должен быть сохранен). Заказ со сложным предикатом занимает некоторое время (поскольку он вызывает внешнюю службу), поэтому я не могу просто дважды переупорядочить коллекцию с ним - второе переупорядочивание должно быть основано на простом индексе.
P.S. Я не ищу менее типобезопасные альтернативы, так как они уже есть в моем проекте :). Моя цель - улучшить существующий (в моем проекте) код.