scala类型不匹配更高的kinded类型

时间:2017-01-10 17:57:34

标签: scala type-mismatch higher-kinded-types type-alias

我想建模Mapper,其中包含A s(T[A])的容器,以便使用f: A => B函数获取另一个容器{{ 1}}。经过几个小时的实验(参见注释代码),我得出了以下解决方案:

T[B]

我现在可以按如下方式使用它:

  sealed trait Mapper[ A, T[ A ], B ] {
    //type Out <: T[B]
    type Out[X] //= T[X]
    def map( l: T[ A ], f: A => B ): Out[B]
  }

  object Mappers {

    implicit def typedMapper[ A, T[ A ] <: Iterable[ A ], B ]: Mapper[ A, T, B ] =
      new Mapper[ A, T, B ] {
        override type Out[X] = Iterable[X]
        //override type Out <: Iterable[ B ]
        //def map( l: T[ A ], f: A => B ) : this.Out = {
        def map( l: T[ A ], f: A => B ) : Out[B] = {
          println( "map" )
          l.map( f )
        }
      }

    implicit def IntMapper = typedMapper[Int, List, Int]
  }

  //def testMapper[ A, T[ A ], B ]( l: T[ A ], f: A => B )( implicit mapper: Mapper[ A, T, B ] ): T[B] = {
  def testMapper[ A, T[ A ], B ]( l: T[ A ], f: A => B )( implicit mapper: Mapper[ A, T, B ] ) : Mapper[A, T, B]#Out[B]= {
    println( mapper )
    mapper.map(l, f)
  }

虽然它有效但我仍然不知道如何直接将Out限制为import Mappers.IntMapper val l9 = testMapper( List( 1, 2, 3 ), { x: Int => x + 1 } ) println(l9) 。如果我这样做,我似乎总是得到类型不匹配。任何人都可以在没有类型别名或直接使用T[B]的情况下指出更清晰/更简单的方法吗?

TIA

1 个答案:

答案 0 :(得分:1)

这是对你想要的评论的第一个近似值。类型成员已被淘汰,在您想要做的事情中显示出稍微深一些的问题。

trait Mapper[A, T[_], B] {
  def map(ta: T[A])(f: A => B): T[B]
}

// Note that Iterable[A]#map has type [B](A => B)Iterable[B]. You can't have typedMapper
// like yours from above just yet, because T#map is not certain to return another T;
// it only promises an Iterable.
implicit def iterableMapper[A, B]: Mapper[A, Iterable, B] = new Mapper[A, Iterable, B] {
  // Multiple param lists support the type inferencer
  override def map(i: Iterable[A])(f: A => B) = i.map(f)
}

// Curried and arg-swapped version of Mapper
type MapperOf[A, B] = { type l[T[_]] = Mapper[A, T, B] }
def map[A, B, T[_]: MapperOf[A, B]#l](ta: T[A])(f: A => B): T[B] = implicitly[Mapper[A, T, B]].map(ta)(f)

map(??? : Iterable[Any])(_.toString) // OK (at compile time, at least :P)
map(List(1,2,3))(_*2) // NOPE! The inferencer has already decided T = List, before
                      // looking for implicits, so the resolution fails to notice that
                      // iterableMapper would work.
map[Int, Int, Iterable](List(1,2,3))(_*2) // Works

这推动了类型推理器的限制,这就是你需要手动指定类型参数的原因。

请注意,Iterable对集合层次结构并不重要。它主要是因为Java拥有它。为了使它对于集合来说是通用的,你需要一些美味的CanBuildFrom黑暗魔法。

import collection._, generic._ // Open the gates of hell

implicit def collectionMapper[A, Coll[A] <: GenTraversableLike[A, Coll[A]], B]
    (implicit builderFactory: CanBuildFrom[Coll[A], B, Coll[B]]):
    Mapper[A, Coll, B] =
  new Mapper[A, Coll, B] {
    override def map(coll: Coll[A])(f: A => B): Coll[B] = coll.map(f)
  }

map(List(1))(_*2): List[Int] // Works
map(Seq(1).view)(_*2): Seq[Int] // Works, but note how we lose the knowledge of the view
                                // Exercise for the reader: fix that.
map(BitSet(1))(_*2): SortedSet[Int] // Works, but we lose the BitSet-ness
                                    // Another exercise: fix it.