scalagenericsreflectionstructural-typing

Will method invocation reflection in Scala cause performance degradation?


I have a method intended to do some simple statistics for data IO, as shown below.

def ioStatSink[T <: { def length: Int }](): Sink[T, Future[(Long, Long)]] = Sink.fold((0L, 0L))((acc, bytes) => (acc._1 + 1L, acc._2 + bytes.length))

As I want it to be able to handle different data types that have a { def length: Int } method, I make it generic.
The problem is, this method invocation uses reflection.
As this method is called millions fo times, I don't want it to have performance issue.
I know class instantiation with reflection has performance penalty, but how about this method invocation?

(another issue about the method is, it cannot adapt to types with a method of { def length: Long }, any suggestion to deal with this?)


Solution

  • Since you said you were interested in the typeclass alternative.
    Here is a simple demo.

    import scala.language.higherKinds
    
    trait Sizeable[T] {
      def size(t: T): Long
    }
    
    object Sizeable {
      private final val _IterableOnceSizable: Sizeable[IterableOnce[_]] =
        new Sizeable[IterableOnce[_]] {
          override final def size(iter: IterableOnce[_]): Long = {
            val knownSize = iter.knownSize
            if (knownSize == -1) iter.iterator.size.toLong
            else knownSize.toLong
          }
        }
    
      implicit final def CollectionSizeable[C[_], T](implicit ev: C[T] <:< IterableOnce[T]): Sizeable[C[T]] =
        _IterableOnceSizable.asInstanceOf[Sizeable[C[T]]]
    }
    
    object syntax {
      object sizeable {
        implicit class SizeableOps[T](private val sizable: T) extends AnyVal {
          @inline def size(implicit ev: Sizeable[T]): Long =
            ev.size(sizable)
    
          @inline def length(implicit ev: Sizeable[T]): Long =
            ev.size(sizable)
        }
      }
    }
    
    import syntax.sizeable._
    
    def ioStatSink[T : Sizeable](): Sink[T, Future[(Long, Long)]] =
      Sink.fold((0L, 0L))((acc, bytes) => (acc._1 + 1L, acc._2 + bytes.length))