1
votes

Comment passer le type à l'exécution à la fonction polymorphe scala

Voici un exemple d'exemple. Où j'ai besoin de convertir la valeur stockée dans String en tant que type et de la transmettre à la fonction polymorphe dans scala.

import scala.reflect.runtime.universe._
import scala.reflect.api

object Test {

    def convert[T](l: String)(implicit typeTag: TypeTag[T]): T = l.asInstanceOf[T]

    implicit def stringToTypeTag[A](name: String): TypeTag[A] = {
        val c = Class.forName(name)
        val mirror = runtimeMirror(c.getClassLoader)
        val sym = mirror.staticClass(name)
        val tpe = sym.selfType
        TypeTag(mirror, new api.TypeCreator {
            def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) =
                if (m eq mirror) tpe.asInstanceOf[U # Type]
                else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
        })
    }

    def main(args:Array[String]): Unit = {
        val typ = "Integer"
        val x = convert("10")(stringToTypeTag("java.lang." + typ))
        val y = convert("20")(stringToTypeTag("java.lang." + typ))

        println(x.getClass)
        println(y.getClass)

        val z = x + y
        println(z)

        // Expected OP 30

        val typ = "String"
        val x1 = convert("10")(stringToTypeTag("java.lang." + typ))
        val y1 = convert("20")(stringToTypeTag("java.lang." + typ))

        println(x1.getClass)
        println(y1.getClass)

        val z1 = x1 + y1
        println(z1)
        // Expected OP 1020

    }
}

OP attendu: 30 quand Integer et 1020 quand String


5 commentaires

Quelle est exactement la question? - Mais surtout, pourquoi? La réflexion d'exécution est extrêmement difficile et dangereuse, globalement le code ne tirera pas parti du système de type avancé de Scala . Ainsi, son utilisation devrait être limitée aux cas où cela est absolument nécessaire. - Si vous pouvez partager votre problème d'origine, nous pouvons peut-être vous recommander une approche différente.


Ceci est un exemple d'exemple pour l'explication. En fait, je charge divers paramètres à partir du fichier de configuration et je dois convertir la valeur en type chargé à partir du fichier de configuration.


Avez-vous jeté un œil à pureconfig ?


Voici mes scénarios complets - args {runDate = {type = int, required = true}, schema = {type = String}. Ceci est chargé en utilisant pure config. Maintenant, je prévois d'utiliser les options existantes parser scpopt ou scallop. Pour créer une option argunemnt, je dois appeler opt [String] ou opt [Int], cette chaîne / int vient de config et je dois les convertir en type afin que je puisse les passer à cette fonction et à un type


Je pense que ce serait mieux si vous fermez cette question et en ouvrez une nouvelle avec votre problème actuel, exemple d'entrée, sortie attendue et tout le code que vous avez essayé.


3 Réponses :


1
votes

Cela ne fonctionnera pas car le polymorphisme des fonctions est effectué au moment de la compilation, pas au moment de l'exécution. Vous ne pouvez donc pas sélectionner une fonction polymorphe basée sur le nom d'un type lu à partir d'un fichier.

Le problème sous-jacent est que le type d'une variable est déterminé au moment de la compilation. Le compilateur doit donc choisir le type de x et y avant que le type soit lu à partir de la configuration. Étant donné que x et y peuvent être de plusieurs types, le compilateur choisit probablement Any .

Ceci est théoriquement correct jusqu'à présent car une variable Any peut contenir soit Int soit String . Mais les choses tournent mal lorsque vous essayez de les ajouter: x + y . Ceci indique au compilateur d'ajouter un Any à un Any et cela va clairement mal tourner. Le programme n'utilise pas le type d'exécution x et y pour sélectionner la fonction + appropriée.


0 commentaires

4
votes

Il y a trop de problèmes dans ce code, je dis juste le plus évident car les problèmes plus profonds nécessitent un blog et non une réponse.

Donc, cette méthode,

trait ConfigReader[A] {
  def read(input: String): A
}

object ConfigReader {

  object Implicits {

    implicit val intConfigReader = new ConfigReader[Int] {
      override def read(input: String): Int = input.toInt
    }

    implicit val doubleConfigReader = new ConfigReader[Double] {
      override def read(input: String): Double = input.toDouble
    }

    implicit val stringConfigReader = new ConfigReader[String] {
      override def read(input: String): String = input
    }

  }

  def read[A](input: String)(implicit configReader: ConfigReader[A]) = configReader.read(input)


}

import ConfigReader.Implicits._

val i = ConfigReader.read[Int]("5")

val d = ConfigReader.read[Double]("5.0")


3 commentaires

Je ne connais pas le type de données avant l'exécution, donc j'aurais besoin d'écrire implicitement pour tous les scénarios ...


Je pense qu'il vaut la peine de préciser qu'il n'y a pas de types à l'exécution, seulement des classes . Et que isInstanceOf fonctionne sur ce dernier. Donc, dans votre exemple, i n'était pas de type Int , c'était Any . Mais sa classe était Int c'est pourquoi le asInstanceOf fonctionne.


@arjunchopra Au moment où vous dites que seul le runtime sait quel type de String est censé être, les choses deviennent alors beaucoup plus compliquées. L'approche sera de soulever les choses dans votre propre «implémentation Repr». Mais ensuite, vous devez vous assurer que votre "implémentation Repr" prend en charge toutes les opérations dont vous avez besoin, et vous ne mappez hors de votre contexte soulevé que lorsque vous n'en avez plus besoin (lors de l'enregistrement dans la base de données ou de l'impression, etc.). Slick a l'un des meilleurs contextes soulevés, mais vous n'aurez besoin de rien d'aussi sophistiqué à distance pour votre objectif.



0
votes

Compte tenu des nouveaux détails des exigences donnés dans les commentaires, vous pouvez faire quelque chose comme suit.

import scala.reflect.ClassTag
import scala.util.{Try, Success, Failure}

case class Repr[+A: ClassTag](value: A)

def readInputAsRepr(
  input: String,
  valueType: String
): Try[Repr[Any]] =
  valueType match {
    case "Int" => Try(Repr[Integer](input.toInt))
    case "Double" => Try(Repr[Double](input.toDouble))
    case "String" => Try(Repr[String](input))
    case _ => Failure(new UnsupportedOperationException)
  }

def intSumAction(i1: Int, i2: Int) = i1 + i2
def doubleSumAction(d1: Double, d2: Double) = d1 + d2
def stringSumAction(s1: String, s2: String) = s1 + s2

def selectAndEvaluateAction(
  repr1: Repr[Any],
  repr2: Repr[Any]
): Try[Repr[Any]] =
  (repr1.value, repr2.value) match {
    case (a1: Int, a2: Int) => Success(Repr(intSumAction(a1, a2)))
    case (a1: Double, a2: Double) => Success(Repr(doubleSumAction(a1, a2)))
    case (a1: String, a2: String) => Success(Repr(stringSumAction(a1, a2)))
    case _ => Failure(new UnsupportedOperationException)
  }

val x1 = readInputAsRepr("10", "Int").get
val y1 = readInputAsRepr("20", "Int").get
val z1 = selectAndEvaluateAction(x1, y1)

val x2 = readInputAsRepr("10", "String").get
val y2 = readInputAsRepr("20", "String").get
val z2 = selectAndEvaluateAction(x2, y2)


0 commentaires