Dynamically parametrize Poly1 function in shapeless
Asked Answered
M

1

7

I have this situation (stripped down to the essential parts)

class Foo[L <: HList](columns: L) {

  class toRecord(row: Row) extends Poly1 {
    implicit def caseColumn[T] = at[Column[T]] { /* map to a record field */ }
  }

  def asRecord = {
    val resultSet: Stream[Row] = // computation to get result set
    resultSet.map { row =>        
      val m = new toRecord(row) // this can't work
      columns.map(m)
    }
  }

}

This doesn't work since map wants a stable identifier and m is not. So I would need as many Poly1 singleton objects as many rows I have in the result set.

This is the same problem discussed here: https://groups.google.com/forum/#!topic/shapeless-dev/P5DXRgnzqkY, but I can't find a way of making this work.

In the linked discussion Miles Sabin proposed a fold with a Poly2 instead of a map with a Poly1, but I can't figure how to use this suggestion in my case.

Miscarry answered 13/8, 2014 at 14:26 Comment(0)
M
5

Ok, I finally managed to use a Poly2 with a foldRight in order to simulate a parametric map.

Here's an example to get the idea

object toRecord extends Poly2 {
  implicit def caseColumn[A, B] = at[Column[A], (Row, B)] {
    case (col, (row, acc)) => 
      val field = doMyThingy(row, col) // "map" `col` to a recordField using `row` as parameter 
      (row, field :: acc)
  }
}

def asRecord[L <: HList, O <: HList](
  implicit folder: RightFolder[L, (Row, HNil.type), toRecord.type, (Row, O)]
): Stream[O] = {
  val resultSet: Stream[Row] = // computation to get result set
  resultSet.map { row => columns.foldRight((row, HNil))(toRecord)._2 }
}

So the "trick" here is to pass the parameter as the initial value of the fold and carry it along during the computation.

Inside the computation we apply a transformation on each element using row as parameter (our "parametric mapping") and then we simply append it to the accumulator.

When we're done, we end up with a tuple containing row and the mapped HList: we can simply discard the former (._2) and we're good to go.

Miscarry answered 14/8, 2014 at 19:29 Comment(2)
Hi Gabriele, thanks for working this out.The example does not work for me (Shapeless 2.3.2): RightFolder has 3 type arguments, not 4, should it be RightFolder.Aux? And the B type param in the caseColumn has to be B <: HList, otherwise the concatenation with :: does not work.Chalkstone
Even with those things fixed, I'm having a hard time creating a working example. Could you be so kind and post a compiling version on ScalaFiddle or Scastie?Chalkstone

© 2022 - 2024 — McMap. All rights reserved.