简体   繁体   中英

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?

Can't you define the implicit class in one of the following ways to get access to the Elem type?

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

or

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

I haven't thoroughly tested this...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM