【发布时间】:2018-01-19 03:51:09
【问题描述】:
假设我有索引集合(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-like 除外)、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 -> "A", 2 - "B", 3 -> "C", 4 -> "D", 5 -> "E" ) 作为输入,分为 (1 -> "A", 3 -> "C", 4 -> "D") 和 (2 -> "B", 5 -> "E"),它们是单独处理,然后合并回来(应保留原始顺序)。使用复杂谓词排序需要一些时间(因为它调用外部服务),所以我不能用它重新排序两次集合 - 第二次重新排序应该基于简单索引。
附:我不是在寻找类型安全性较低的替代方案,因为我的项目中已经有这样的替代方案:)。我的目标是改进现有(在我的项目中)代码。
【问题讨论】:
-
好的,我可能遗漏了一些非常明显的东西......如果我需要防止用户弄乱我的数据表示,那么我将它隐藏在一个类中并公开只允许的操作。跨度>
-
@jwvh 如果您仔细阅读 - 您会发现这就是我所做的 + 其他几个步骤,以确保用户不会创建另一个集合。当然,我可以关闭构造函数,但实际上我也是该库的用户,并且仍然希望能够创建集合,但要确保它没有被另一个替换。所以我也为此做了一些步骤(见上文)并且它有效。我的问题是:它是否已经在 scalaz 或其他地方实现了,因为可能很多人想要阻止用户重新排序,所以我不想重新发明轮子
标签: scala scalaz type-safety