如何避免丢失类型信息

时间:2014-07-29 08:11:40

标签: scala shapeless

假设我有这样的事情:

trait Cursor {
}

trait Column[T] {
   def read(cusor: Cursor): T
}

trait ColumnReader {
   def readColumns(columns: Product[Column[_]], cursor: Cursor): Iterable[Any] = {
       for (column <- columns) yield column.read(cursor)
   }
}

readColumns() API的问题是我丢失了类型信息,即如果我有这个:

object columnString extends Column[String] {
   def read(cursor: Cursor): String = ...
}

object columnInt extends Column[Int] {
   def read(cursor: Cursor): Int = ...
}

new ColumnReader().readColumns((columnString, columnInt))这样的表达式会返回Iterable[Any]。我想返回类似Tuple2[String, Int]的内容,但不知道如何。我丢失了对编译器有用的类型信息。

也许像Shapeless这样的图书馆很有用。

我确信Scala有一些处理这类问题的工具。

有什么想法吗?

4 个答案:

答案 0 :(得分:4)

使用无形HList

的示例
class Cursor

trait Column[T] {
  def read(cusor: Cursor): T
}

class CursorContainer(cursor: Cursor) {
  object mapper extends Poly1 {
    implicit def any[T] = at[Column[T]](_.read(cursor))
  }
}

class ColumnReader {

  def readColumns[T <: HList](columns: T, cursor: CursorContainer)(
    implicit m:Mapper[cursor.mapper.type, T]) = columns.map(cursor.mapper)
}

val columnString = new Column[String] {
  def read(cursor: Cursor): String = ???
}

val columnInt = new Column[Int] {
  def read(cursor: Cursor): Int = ???
}

val reader = new ColumnReader
val cursor =  new CursorContainer(new Cursor)
val result: String :: Int :: HNil =
  reader.readColumns(columnString :: columnInt :: HNil, cursor)

答案 1 :(得分:1)

为什么不使用可以采用类型参数的容器,例如Seq或List?

trait Cursor {
}

trait Column[T] {
   def read(cusor: Cursor): T
}

trait ColumnReader[T] {
   def readColumns(columns: Seq[Column[T]], cursor: Cursor): Iterable[T] = {
       for (column <- columns) yield column.read(cursor)
   }
}

答案 2 :(得分:1)

你需要一个来自Shapeless的HList

答案 3 :(得分:1)

如果列数有限,您也可以使用Applicative。

trait Column[T] {
  def read(c: Cursor) : Id[T]
}
object columnString extends Column[String]
{
  override def read(c: Cursor): Id[String] = "hello"
}

object columnInt extends Column[Int] {
  override def read(c: Cursor): Id[Int] = 3
}

type ColumnReader[T] = Reader[Cursor, T]

val readSpreadSheet1 : ColumnReader[(Int, String)] = Reader {
  c =>
    (columnInt.read(c) |@| columnString.read(c)) { (_,_)}
}

readSpreadSheet1(c)

会导致:

res1: scalaz.Id.Id[(Int, String)] = (3,hello)

我还抛出了一个小的Reader定义,这样当你读取一行时你不需要关心传递光标的实例。在缺点方面,您需要预先知道列的类型,但我认为当您使用HList时也是如此。