【问题标题】:How to make sure (in compile-time) that collection wasn't reordered?如何确保(在编译时)该集合没有重新排序?
【发布时间】:2018-01-19 03:51:09
【问题描述】:

假设我有索引集合(ListMap 在这里无关紧要):

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)

但我需要限制用户,只做mapfiltercollectcontainsforallscanLeft 等等。但不是flatMapfilter-like 除外)、sort++ 等等。所以任何双射/超射,但不是类似注射的。在紧要关头,用户可以在没有filter/flatMap 的情况下生活,因此拥有Functor,但不是Monad 对我来说可能没问题-无论如何我最初的要求可接受的List[Option[T]] => List[T] 并不完整MonadM[M[T]] => M[T], T => M[T])。

toListtoSet 是可以接受的,但我也想确保返回(从业务处理程序)集合是基于原始集合。我可以通过将路径相关的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


【解决方案1】:

也许这可以通过使用 shapeless 的 NatHList 来解决?这个想法是显式地对元素的索引进行建模。 (我问了一个相关的问题Achieving compile safe indexing with Shapeless。)例如,

import shapeless._
import Nat._

case class Entry[+N<:Nat](value: String, index: N)

val send: Entry[ _1] :: Entry[ _3] :: Entry[ _4] :: HNil= Entry("A", _1):: Entry("C", _3) :: Entry("D", _4) :: HNil
val keep= Entry("B", _2) :: Entry("E", _5)  :: HNil

这将提供一些类型安全性(尽管我不确定性能影响。)

【讨论】:

  • +1 我喜欢使用Nat 的想法,而且 HList 本身的性能对我来说并不重要;然而,一个实际问题是,在我的情况下隐藏索引和连接会很好 - 让我们说Entry("Z", _1) :: keep 或在 N 上编写一个多态函数(如.map(_.copy(index = _3)))非常容易。附言事实上,我在 3 年前离开了那个项目,据我所知,我的 NonReorderedList 似乎一直存活到现在,但我的无形代码(HList 和自然转换)肯定没有:)。
  • 嗯,我明白了。感谢您对 HLists 使用的长期看法 - 我意识到我应该真正隔离代码中的无形位。
猜你喜欢
  • 1970-01-01
  • 2019-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-17
相关资源
最近更新 更多