lampepfl / dotty-feature-requests

Historical feature requests. Please create new feature requests at https://github.com/lampepfl/dotty/discussions/new?category=feature-requests
31 stars 2 forks source link

Selectable does not pass type information to allow precise conversion #339

Closed wiltonlazary closed 1 year ago

wiltonlazary commented 1 year ago

https://kotlinlang.org/docs/delegated-properties.html

  trait DbmDoc(val $doc: Doc = Doc()) extends Selectable {
    def selectDynamic(name: String/*,  returnClass: Class[_] */): Any = {
      val raw = $doc.get(name) 
      raw //Missing result declared type for precise conversion so, "x.asInstanceOf[Id]" will fail
    }

    //paramClasses give the precise information
    def applyDynamic(name: String, paramClasses: Class[_]*)(args: Any*): Any = {
      $doc.put(name, args(0))
    }
  }

  type DbmEntity = DbmObj {
    def id: Id[this.type]
    def id_=(x: Id[this.type]): Unit
  }
bishabosha commented 1 year ago

in scala you can only have 1 value with a given name, but many overloaded methods. (The idea for adding paramClasses in apply dynamic is only for differentiation of overloads.) Is the idea that you require some sort of transformation before you return the selected value?

bishabosha commented 1 year ago

you could try adding a type class parameter to do the conversion

trait DbmDoc(val $doc: Doc = Doc()) extends Selectable {
  def selectDynamic[T: [U] =>> Conversion[Any, T]](name: String): T = {
    val raw = $doc.get(name) 
    raw: T
  }
  ...
}
wiltonlazary commented 1 year ago

Inside $doc i have arbitrary Bson values, so, i need to know the destiny type to convert properly, ie: the bson type may be an Array[Byte] and the destiny type a String, so i need to call String(bytes) or may be the destiny is a Array[Byte] it's self, then I don't need to convert.

May be I have a String inside the Bson, but the destiny type is a Long, so a need to call (raw: String).toLong .

the scenary "from => to" is flexible, because the data comes from a schema-less database.

bishabosha commented 1 year ago

ok, well I tried out my example above and adding [T] type parameter does capture the result type correctly, so you can require e.g. def selectDynamic[T](name: String)(using ct: reflect.ClassTag[T]): T = ... and get the runtime class that way

wiltonlazary commented 1 year ago

It's worked in that way:

object AppModule:
  opaque type DbmDocValue = Any

  trait DbmDoc(val $doc: MutMap[String, Any] = MutMap()) extends Selectable {
    def selectDynamic[T: [U] =>> Conversion[DbmDocValue, T]](name: String): T = {
      $doc.getOrElse(name, null)
    }
  }

  class DbmObj($doc: MutMap[String, Any] = MutMap()) extends DbmDoc($doc)

  type DbmEntity = DbmDoc {
    def id: Id[this.type]
    def id_=(x: Id[this.type]): Unit
  }

@main def JvmMain(): Unit = {
    appMain()
}

def appMain() = {
  import AppModule.{*, given}

  given Conversion[DbmDocValue, Id[Any]] = { it =>
    val value = (it: Any)

    value match {
      case null         => Id.EMPTY
      case x: ByteArray => Id(x)
      case x: String    => Id(x.getBytes())
      case _            => throw RuntimeException(s"conversion not implemented: ${value.getClass()} => Id")
    }
  }

  val ent: DbmEntity = DbmObj(MutMap("id" -> "sol_xx1_000_0_0_1_1GM0PFTN8_2_2")).as[DbmEntity]
  val id = ent.id
  println(id.toStrand)
}
wiltonlazary commented 1 year ago

thanks @bishabosha, may be It can become an example to inform people coming from Kotlin how-to map advanced language features.