scalashapelesstype-level-computationframeless

Is there a way to implicitly get instance of an object


We want to create an encoder for arbitrary Enumerations with frameless which
is basically creating a bidirectional mapping from an arbitrary Enumeration to Byte. Currently our less than optimal solution is to give evidence on all of our Enumeration instances so the deserializer can pick it up and call apply on that instance, a method that creates an Enumeration from a Byte. We want to find a way without defining these implicit values, and rather have them automatically picked up from the E type. As far as we know object types are in one-to-one correspondence to a single instance, so we hope there's a mechanism to do this.

For example, the following works

import frameless._

object Color extends Enumeration {
  type Color = Value
  val Red, Green, Blue = Value
}
object State extends Enumeration {
  type State = Value
  val Running, Stopped, Finished = Value
}

implicit val colorEvidence = Color // we want to spare these lines
implicit val stateEvidence = State // we want to spare these lines

implicit def enumToByteInjection[E <: Enumeration](implicit e: E): 
  Injection[E#Value, Byte] = Injection(_.id.toByte, e.apply(_))

Solution

  • Solution 1 (reflection)

    This here compiles and runs when compiled with scalac 2.12.4:

    object Color extends Enumeration {
      type Color = Value
      val Red, Green, Blue = Value
    }
    object State extends Enumeration {
      type State = Value
      val Running, Stopped, Finished = Value
    }
    
    /** Dummy replacement with similar signature */
    class Injection[A, B]()
    
    
    import scala.reflect.runtime.universe.TypeTag
    
    object ItDoesNotWorkInReplObjectsMustBeTopLevel {
    
      implicit def enumToByteInjection[E <: Enumeration](implicit tt: TypeTag[E]): Injection[E#Value, Byte] = {
        val ru = scala.reflect.runtime.universe
        val classLoaderMirror = ru.runtimeMirror(getClass.getClassLoader)
        val moduleSymbol = ru.typeOf[E].termSymbol.asModule
        val moduleMirror = classLoaderMirror.reflectModule(moduleSymbol)
        val companionObject = moduleMirror.instance.asInstanceOf[E]
    
        println(s"/* 1 */ Materialize companion object $companionObject out of nothing!")
                  /* 2 */ ???
                  /* 3 */ // profit!
      }
    
      /** A function that requires implicit `Injection` */
      def testNeedsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit = 
        println("replace ??? above to continue here")
    
    
      def main(args: Array[String]): Unit = {
        /** Test whether an implicit injection is constructed */
        testNeedsInjection[Color.type] // compiles (crashes, as expected, but compiles)
    
      }
    }
    

    It of course crashes because of the missing implementation at ???, but this comes after the implicit companion object is summoned into existence.

    Gotchas:


    Solution 2 (implicit objects)

    If you have all enums under your control, then you could simply declare the enumeration objects themselves implicit. The following compiles just nicely, all implicits are inserted as expected:

    implicit object Color extends Enumeration {
      type Color = Value
      val Red, Green, Blue = Value
    }
    implicit object State extends Enumeration {
      type State = Value
      val Running, Stopped, Finished = Value
    }
    
    /** Dummy replacement with similar signature */
    class Injection[A, B]()
    
    implicit def enumToByteInjection[E <: Enumeration](implicit e: E): Injection[E#Value, Byte] = ???
    
    /** A function that requires implicit `Injection` */
    def needsInjection[E <: Enumeration](implicit inj: Injection[E#Value, Byte]): Unit = ???
    
    /** Test whether an implicit injection is constructed */
    needsInjection[Color.type] // compiles (crashes, as expected, but compiles)