Search code examples
scalashapeless

How to avoid losing type information


Suppose I have something like this:

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)
   }
}

The problem of the readColumns() API is that I lose the type information, i.e., if I have this:

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

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

An expression like new ColumnReader().readColumns((columnString, columnInt)) returns Iterable[Any]. I would like to return something typed like Tuple2[String, Int], but don't know how. I lose type information useful to the compiler.

Maybe a library like Shapeless could be useful.

I'm sure Scala has some tool for dealing on problems like this.

Any ideas?


Solution

  • An example using a shapeless 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)