【问题标题】:Cast (or rather: ascribe) collection type without specifying type parameter在不指定类型参数的情况下强制转换(或者更确切地说:归属)集合类型
【发布时间】:2012-09-29 14:27:18
【问题描述】:

假设我有一个 ListInts

scala> val list = List(1, 2, 3, 4, 5)
list: List[Int] = List(1, 2, 3, 4, 5)

当然,Scala 足够聪明,可以为我返回正确的类型。现在,考虑到,我对List 的特定功能不感兴趣,而是希望有一个更通用的(超级)类型,比如Traversable。显然,我可以指定它:

scala> val trav = list: Traversable[Int]
trav: Traversable[Int] = List(1, 2, 3, 4, 5)

但这意味着我还必须重复类型参数。

这行不通

scala> list : Traversable
<console>:9: error: type Traversable takes type parameters
       list : Traversable
              ^

而且这两个例子都删除了类型参数信息

scala> list : Traversable[T forSome {type T}]
res2: Traversable[T forSome { type T }] = List(1, 2, 3, 4, 5)

scala> list : Traversable[_]
res3: Traversable[Any] = List(1, 2, 3, 4, 5)

有没有无需输入Int 即可获得Traversable[Int] 的方法?

【问题讨论】:

    标签: scala collections type-parameter


    【解决方案1】:

    您所要求的基本上是一个多态函数;或更高种类的函数。您可以在类型 * -> * 之间定义这样的映射,如下所示:

    scala> trait ~>[F[_], G[_]] { def map[A](f: F[A]): G[A] }
    defined trait $tilde$greater
    

    你当然需要一个隐式实例

    scala> implicit object ListIsTrav extends (List ~> Traversable) { 
      | def map[A](l: List[A]): Traversable[A] = l 
      | }
    defined module ListIsTrav
    

    现在为 * -> *

    形式的类型添加转换器
    scala> class Homomorphic[F[_], A](f: F[A]){  
      | def as[G[_]](implicit ev: F ~> G): G[A] = ev map f 
      | }
    defined class Homomorphic
    
    scala>  implicit def Type_Is_Homomorphic[F[_], A](f: F[A]) = new Homomorphic(f)
    Type_Is_Homomorphic: [F[_], A](f: F[A])Homomorphic[F,A]
    

    现在使用它:

    scala> List(1, 2, 3).as[Traversable]
    res0: Traversable[Int] = List(1, 2, 3)
    

    这里的痛苦是List ~&gt; Traversable 排序的隐式实例呈指数级增长。它在实践中并不那么有用。

    【讨论】:

    • 非常有见地。其实我原本只对F[A] &lt;:&lt; G[A]这个case感兴趣,所以没有隐式爆炸。
    【解决方案2】:

    这可行,但要避免输入 Int 需要做很多工作......也许有人可以在此基础上想出一个更简洁的技巧。

    scala> def traversableId[T](t: Traversable[T])= t
    traversableId: [T](t: Traversable[T])Traversable[T]
    
    scala> traversableId(list)
    res1: Traversable[Int] = List(1, 2, 3, 4, 5)
    

    【讨论】:

      【解决方案3】:

      虽然oxbow_lakes 给出了一个great solution 关于如何解决任意组合问题的问题,但他启发了我如何解决TraversableList 的超类型的情况。 (不过,这个问题最初并没有具体说明。)

      class CastToSuperType[F[_], A](f: F[A]) {
        def as[G[_]](implicit ev: F[A] <:< G[A]): G[A] = f: G[A]
      }
      
      implicit def implCastToSuperType[F[_], A](f: F[A]) = new CastToSuperType(f)
      
      scala> List(1, 2, 3).as[Traversable]
      res0: Traversable[Int] = List(1, 2, 3)
      

      我怀疑,具有任意数量类型参数的类型可能需要类型 lambda。

      【讨论】:

      • 如果你想查看 Map[String, List[Int]] 作为 Map[String, Traversable[Int]],你需要输入 lambdas
      【解决方案4】:

      写一个对应的方法:

      def asTraversable[T](x: Traversable[T]) = x
      
      asTraversable(x)
      

      【讨论】:

        【解决方案5】:

        集合类对大多数通用特征都有转换方法

        scala> List(1,2,3).toTraversable
        res0: Traversable[Int] = List(1, 2, 3)
        
        scala> List(1,2,3).toIterable
        res1: Iterable[Int] = List(1, 2, 3)
        
        scala> List(1,2,3).toIndexedSeq
        res2: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
        

        【讨论】:

        • 我知道。问题更多的是从句法的角度来看一般情况下什么是可能的。
        【解决方案6】:

        在 scala 2.10 中它要简单得多,因为引入了 .to[Col[_]],它是 scala.collection.TraversableLike 的一部分

        这是定义:

        def to[Col[_]](implicit cbf : scala.collection.generic.CanBuildFrom[scala.Nothing, A, Col[A]]) : Col[A]
        

        所以你基本上可以这样做:

        scala> List(1, 2, 3, 4, 5).to[Vector]
        res0: Vector[Int] = Vector(1, 2, 3, 4, 5)
        
        
        scala> List(1, 2, 3, 4, 5).to[Set]
        res1: Set[Int] = Set(5, 1, 2, 3, 4)
        

        【讨论】:

          猜你喜欢
          • 2020-10-11
          • 2021-09-19
          • 2020-09-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-14
          • 2023-04-01
          • 1970-01-01
          相关资源
          最近更新 更多