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 with the readColumns() API is that I am losing 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 of type new ColumnReader().readColumns((columnString, columnInt)) returns Iterable[Any] . I would like to return something printed as Tuple2[String, Int] , but I don’t know how to do it. I am losing type information useful to the compiler.

Maybe a library like Shapeless might be useful.

I am sure Scala has some tool to solve such problems.

Any ideas?

+6
source share
4 answers

An example of 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) 
+4
source

Why not use a container that can accept type parameters, for example. Seq or 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) } } 
+1
source

You need an HList from Shapeless

+1
source

You can also use applicative if the number of columns is limited.

 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) 

will result in:

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

I also chose a small Reader definition, so you don't have to worry about passing around the cursor instance when reading a line. On the other hand, you need to know the types of your columns in advance, but I think this will also be true if you use HList.

+1
source

Source: https://habr.com/ru/post/973017/


All Articles