mercredi 18 mai 2016

How to call a generic method with a runtime type parameter in Scala?

Background

I am using Akka Streams and I want to add some extension methods to Flow[In, Out, Mat] which are specifically for when Out is a Seq.

I am starting with this trait to make it easy to get at the type fields:

trait StageOps[+Out, +Mat] {
  protected val stage: FlowOps[Out, Mat]
  type Repr[+O] = stage.Repr[O]
  type Closed = stage.Closed
}

Then I have this (more details to follow later):

trait FramingOps[Out <: Seq[_], Mat] extends StageOps[Out, Mat] {
  def frame(start: Out, end: Out): Repr[Out] = {
    ...
  }
}

And now the implicit conversion:

implicit class SeqFlowOps[In, Out <: Seq[_], Mat](val stage: Flow[In, Out, Mat])
  extends FramingOps[Out, Mat] {}

This worked great, and I can do something like:

val byteFlow: Flow[ByteString, ByteString, _] = ??? // Some flow from somewhere else
byteFlow.frame(ByteString(0x0B), ByteString(0x1C, 0x0D)) // Implicit conversion

Problem

The implementation of FramingOps#frame has evolved where I need to do something like this:

def frame(start: Out, end: Out): Repr[Out] = {
  val maxBufferSize = math.max(start.length, end.length) - 1
  val checkForMalformedChunk = stage.statefulMapConcat[Out](() => {
    var buffer = Seq.empty[Any]
    (chunk: Out) => {
      buffer = buffer ++ chunk
      if (buffer.containsSlice(start) || buffer.containsSlice(end)) ??? // Bad encoding!
      buffer = buffer.takeRight(maxBufferSize)
      immutable.Seq(chunk)
    }
  })
  checkForMalformedChunk.prepend(Source.single(start)).concat(Source.single(end))
}

Having to use var buffer = Seq.empty[Any] is ok for now but I'm sure I can do better.

This attempt breaks the implicit conversion as there is no way to provide the element type:

trait FramingOps[Elem, Out <: Seq[Elem], Mat] extends StageOps[Out, Mat] { ... }

So I figured using a TypeTag is probably the only option:

abstract class FramingOps[Out <: Seq[_] : TypeTag, Mat] extends StageOps[Out, Mat] { ... }

Now I can get the element type at runtime by using:

implicitly[TypeTag[Out]] match { case TypeRef(_, _, List(elemType)) => elemType }

But how do I use this to create the empty sequence of the right type? Do I need to keep using reflection and provide the type parameter myslef? If so how do I do that?





Aucun commentaire:

Enregistrer un commentaire