Let's say I have indexed collection (List
or Map
doesn't matter here):
val zipped = List(1 -> "A", 3 -> "C", 8 -> "D")
It's hard to work with such (as every operation, like map
, has to deal with index
), so what I want to pass into business handler is:
case class Indexed[T](seq: Seq[T], i: Seq[Int])
val unzipped = Indexed(List("A", "C", "D"), List(1,3,8))
handler(unzipped.seq)
But I need user to be restricted, to do only map
, filter
, collect
, contains
, forall
, scanLeft
and so on. But not flatMap
(except filter
-like), sort
, ++
and so forth. So any bijection/surjection, but not injection-like. In a pinch, user can live without filter
/flatMap
, so having Functor
, but not Monad
could be fine for me - anyway acceptable List[Option[T]] => List[T]
from my original requirements isn't complete Monad
(M[M[T]] => M[T]
, T => M[T]
).
toList
, toSet
are acceptable, but I also want to be sure that returning (from business handler) collection is based on original one. I can do this by adding path-dependent Tag
(path-dependent type) to the type signature of original collection and require same-tagged collection as a return type (which can be cheated only with asInstanceOf
). My first requirements can be satisfied by implementing my own Traversable
.
So my own "wheel" to solve this is just a wrapper (with only subset of operations permitted + tags for making sure that collection is same):
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)
}
}
Here is the results from 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)
^
However, it shouldn't be so rare situation when you need such collection, so maybe Scalaz already has some implementation, something like NonEmptylist
, but NonReorderedList
.
I'm doing all this because I have one already ordered collection (1 -> "A", 2 - "B", 3 -> "C", 4 -> "D", 5 -> "E") as an input, which splits into (1 -> "A", 3 -> "C", 4 -> "D") and (2 -> "B", 5 -> "E"), they're processed separately and then merged back (original order should be preserved). Ordering with complex predicate takes some time (as it calls to the external service), so I can't just reorder collection twice with it - second reordering should be based on simple index.
P.S. I'm not looking for less type-safe alternatives as I already have such in my project :). My goal is to improve existing (in my project) code.