J'ai un trait dans Scala qui a une seule méthode. Appelez-le calculable et la méthode unique est calculée (entrée: int): int. Je ne peux pas comprendre si je devrais p>
Un facteur en faveur d'une traite est que je pourrais u utilement ajouter des méthodes supplémentaires. Mais bien sûr, s'ils étaient tous mis en œuvre en termes de méthode de calcul, je pouvais simplement les casser dans un objet distinct. P>
Un facteur en faveur de l'utilisation d'une simple utilisation du type de fonction est la simplicité et le fait que la syntaxe d'une fonction anonyme est plus concise que celle d'une instance informatique anonyme. Mais alors je n'ai aucun moyen de distinguer les objets qui sont en réalité des instances calculables d'autres fonctions qui ne sont pas destinées à être utilisées dans le même contexte que calculable. P>
Comment les autres personnes s'approchent-elles ce type de problème? Aucune bonne ou mauvaise réponse ici; Je cherche juste des conseils. P>
4 Réponses :
On sonne comme si vous voudrez peut-être utiliser un Type de structure . Ils sont également appelés interfaces implicites. P>
Vous pouvez ensuite refroidir les méthodes qui acceptent actuellement un calculable code> pour accepter tout ce qui comporte un calcul (entrée: int) code> méthode. p>
D'accord, cela rendrait un objet qui a une méthode de calcul utilisable en tant que calculable. Mais ce que j'aimerais plus intéressé par la connaissance est, quelles sont quelques considérations pour ou contre la fabrication d'un type de fonction calculable? Un problème que j'ai constaté est que si calculable est un type de fonction, des classes qui implémentent ne peuvent pas être d'autre type de fonction. Si j'avais une autre interface, affichée, qui héritée de (string => string), une classe n'a pas pu être calculable et affichée car elle n'hériterait pas de fonction1 des deux traits, mais avec des paramètres de type différents.
C'est vrai, c'est un argument contre l'extension (ou l'utilisation directe) int => int. Si vous vous attendez à ce que ce soit le cas d'utilisation typique, un nom personnalisé pourrait être meilleur.
Une option est de définir un type (vous pouvez toujours l'appeler calculable), ce qui est pour le moment est int => int. Utilisez-le chaque fois que vous avez besoin du matériel calculable. Vous obtiendrez tous les avantages d'héritage de la fonction1. Ensuite, si vous réalisez que vous avez besoin de plusieurs méthodes, vous pouvez modifier le type à un autre trait.
au début: p> plus tard sur: p> Un inconvénient est que le type que vous avez défini est que le type que vous avez défini est Pas vraiment un nouveau type, plus une sorte d'alias. Ainsi, jusqu'à ce que vous changiez à un trait, le compilateur acceptera toujours d'autres fonctions INT => INT. Au moins, vous (le développeur) peut différencier. Lorsque vous changez à un trait (et que la différence devient importante), le compilateur découvrira lorsque vous avez besoin d'un calculable mais qu'il a un int => int. P> si vous voulez que le compilateur rejette l'autre int => int -s du premier jour, je recommanderais ensuite d'utiliser un trait, mais prolonger int => int. Lorsque vous devez l'appeler, vous auriez toujours la syntaxe plus pratique. P> Une autre option peut être d'avoir un trait et un objet compagnon avec une méthode d'application qui accepte un int => int et crée un ordinateur calculable de ça.
Ensuite, la création de nouveaux ordinateurs serait presque aussi simple que d'écrire des fonctions anonymes simples, mais vous auriez toujours le type de vérification (que vous perdriez avec une conversion implicite). De plus, vous pouvez mélanger dans le trait sans problèmes (mais la demande de l'objet compagnon ne peut pas être utilisée tel quel). P> P>
Si vous en faites un trait et que vous souhaitez toujours pouvoir utiliser la syntaxe de fonction légère, vous pouvez également ajouter une conversion implicite dans les endroits où vous les souhaitez:
scala> trait Computable extends (Int => Int)
defined trait Computable
scala> def computes(c: Computable) = c(5)
computes: (c: Computable)Int
scala> implicit def toComputable(f: Int => Int) = new Computable { def apply(i: Int) = f(i) }
toComputable: (f: (Int) => Int)java.lang.Object with Computable
scala> computes( (i: Int) => i * 2 )
res0: Int = 10
J'utilise une approche très similaire. J'ai des traits courbe s'étend (double => double) code> et la surface s'étend ((double, double) => double) code>, avec implicite pour soulever des fonctions Scala normales. Si vous définissez la conversion dans l'objet compagnon de votre trait calculable code>, il est automatiquement dans la portée implicite lors de la recherche d'une vue à partir de n'importe quel type t code> à Computable < / code>.
C'est en fait ce que j'ai fait. Mais je me demande ce que le trait calculable "achète" moi. Si je l'utilise toujours comme (int => int) alors pourquoi déranger du tout? D'après ce que je peux dire, il n'y a qu'une prestation documentaire.
Si c'est juste pour la documentation, vous pouvez aussi définir un alias de type: type calculable = int => int code> ..
Créer un trait qui s'étend à partir d'un type de fonction peut être utile pour quelques raisons. P>
Votre objet de fonction fait quelque chose de spécial et non évident (et difficile à taper), et vous pouvez paramétrer de légères variations dans un constructeur. Par exemple, supposons que vous écriviez un trait pour effectuer une requête XPath sur un arbre XML. La fonction Apply serait masquer plusieurs types de travail dans la construction du mécanisme de la requête XPath, mais il est toujours intéressant de mettre en vaut la peine d'implémenter l'interface code> fonction1 code> afin que vous puissiez interroger à partir d'un tas de nœuds différents à l'aide de la carte comme une extension du n ° 1, vous voulez faire du traitement à la durée de la construction (par exemple, l'analyse de l'expression XPath et la compiler pour fonctionner rapidement), vous pouvez faire une fois, à l'avance, dans le constructeur de l'objet ( tandis que si vous venez de curry Vous voulez passer une fonction de cryptage (un type de J'espère que c'était clair. p> code> ou platmap code>. p> li>
fonction1 [String, String] code>) comme implicite, mais pas tous fonction1 [chaîne, string] code > S Effectuer un cryptage. En dérivant de fonction1 [chaîne, string] code> et nommer la sous-classe / trait cryptionfonction code>, vous pouvez vous assurer que seules les fonctions de la sous-classe droite seront adoptées implicitement. (Ce n'est pas vrai lorsque vous déclarez type cryptionfunction = string => chaîne code>.) P> li>
ol>