8
votes

Trait, fonction ou fonctionnement hérité de trait dans SCALA?

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

  • Laissez-le comme un trait autonome avec une seule méthode.
  • hériter de (int => int) et renommer "calculer" à "appliquer".
  • se débarrasser de calculable et d'utiliser (int => int).

    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.

    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.

    Comment les autres personnes s'approchent-elles ce type de problème? Aucune bonne ou mauvaise réponse ici; Je cherche juste des conseils.


0 commentaires

4 Réponses :


1
votes

On sonne comme si vous voudrez peut-être utiliser un Type de structure . Ils sont également appelés interfaces implicites.

Vous pouvez ensuite refroidir les méthodes qui acceptent actuellement un calculable pour accepter tout ce qui comporte un calcul (entrée: int) méthode.


2 commentaires

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.



1
votes

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: xxx

plus tard sur: xxx

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.

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.

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).


0 commentaires

8
votes

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


3 commentaires

J'utilise une approche très similaire. J'ai des traits courbe s'étend (double => double) et la surface s'étend ((double, double) => double) , avec implicite pour soulever des fonctions Scala normales. Si vous définissez la conversion dans l'objet compagnon de votre trait calculable , il est automatiquement dans la portée implicite lors de la recherche d'une vue à partir de n'importe quel type t à 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 ..



3
votes

Créer un trait qui s'étend à partir d'un type de fonction peut être utile pour quelques raisons.

  1. 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 fonction1 afin que vous puissiez interroger à partir d'un tas de nœuds différents à l'aide de la carte ou platmap .

  2. 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 fonction S sans sous-classement, la compilation ne pouvait se produire qu'au moment de l'exécution, il serait donc répété pour chaque requête.)

  3. Vous voulez passer une fonction de cryptage (un type de fonction1 [String, String] ) comme implicite, mais pas tous fonction1 [chaîne, string] S Effectuer un cryptage. En dérivant de fonction1 [chaîne, string] et nommer la sous-classe / trait cryptionfonction , 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 .)

    J'espère que c'était clair.


0 commentaires