Question

Je voudrais savoir comment faire les types de membres travaillent à Scala, et comment dois-je associer types.

Une approche est de rendre le type associé un paramètre de type. Les avantages de cette approche est que je peux prescrire la variance du type, et je peux être sûr qu'un sous-type ne change pas le type. Les inconvénients sont, que je ne peux pas déduire le paramètre de type de type dans une fonction.

La seconde approche est de rendre le type associé un membre du second type, qui a le problème que je ne peux pas prescrire les limites sur les types associés de sous-types et, par conséquent, je ne peux pas utiliser le type dans les paramètres de fonction ( lorsque x: X, X # T pourrait ne pas être dans une relation avec xt)

Un exemple concret serait:

J'ai un trait pour DFA (peut-être sans le paramètre de type)

trait DFA[S] { /* S is the type of the symbols in the alphabet */
  trait State { def next(x : S); }
  /* final type Sigma = S */
}

et je veux créer une fonction pour l'exécution de ce DFA sur une séquence d'entrée, et je veux

  • la fonction doit prendre quelque chose <% Seq[alphabet-type-of-the-dfa] que le type de séquence d'entrée
  • l'appelant la fonction ne doit pas spécifier les paramètres de type, tout doit être inférée
  • Je voudrais que la fonction soit appelée avec le type de béton DFA (mais s'il y a une solution où la fonction ne serait pas un paramètre de type pour la DFA, il est OK)
  • les types d'alphabet doivent être sans contrainte (ie. Il doit y avoir un DFA pour Char, ainsi que pour une classe définie par l'utilisateur encore inconnu)
  • les DFA avec différents types d'alphabet ne sont pas des sous-types

J'ai essayé ceci:

def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = ....

cela fonctionne, à l'exception du type S ne déduit ici, donc je dois écrire toute la liste des paramètres de type sur chaque site d'appel.

def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]]( ... same as above

cela ne fonctionne pas (référence non valide circulaire de type D ??? (quel est-il?))

J'ai également supprimé le paramètre de type, créé un type abstrait Sigma et essayé lier ce type dans les classes concrètes. runDFA ressemblerait

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]]( ... same as above

mais cela va inévitablement des problèmes comme "incompatibilité de type: dfa.Sigma attendu, obtenu D#Sigma"

Toutes les idées? Pointeurs?

Edit:

Comme les réponses indiquent qu'il n'y a pas moyen simple de faire cela, quelqu'un pourrait-il parler plus en détail pourquoi il est impossible et ce qui devrait être changé si cela a fonctionné?

Les raisons pour lesquelles je veux runDFA ro une fonction libre (pas de méthode) est que je veux que d'autres fonctions similaires, comme la minimisation automate, les opérations linguistiques régulières, conversions NFA-à-DFA, la langue factorisation etc., et ayant tout cela à l'intérieur d'une classe est juste contre presque tout principe de la conception orientée objet.

Était-ce utile?

La solution

Tout d'abord, vous n'avez pas besoin de la SQ paramétrisation <% Seq [S]. Ecrire le paramètre Méthode Seq [S]. Si SQ <% Seq [S] alors toute instance de c'est implicitement convertable Seq [S] (c'est ce que <% signifie), de sorte que lorsqu'elle est transmise comme Seq [S] le compilateur insère automatiquement la conversion.

De plus, ce que Jorge a dit à propos des paramètres de type D et sur ce qui en fait une méthode en attente DFA. En raison de la façon dont les classes internes travaillent dans Scala je conseille fortement mettre runDFA sur DFA. Jusqu'à ce que la substance de frappe en fonction du chemin fonctionne, traitant des classes internes d'une classe externe peut être un peu d'une douleur.

Alors maintenant, vous avez

trait DFA[S]{
  ...

  def runDFA(seq : Seq[S]) = ...
}

Et runDFA est tout d'un coup assez facile de déduire des paramètres de type pour: Il n'a pas.

Autres conseils

inférence de type Scala laisse parfois à désirer.

Y at-il des raisons pour lesquelles vous ne pouvez pas avoir la méthode dans votre trait DFA?

def run[SQ <% Seq[S]](seq: SQ)

Si vous n'avez pas besoin D plus tard param, vous pouvez aussi essayer de définir votre méthode sans elle:

def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ...

Quelques informations utiles sur la façon dont les deux diffère:

De la guider:

Sans paramètres de type que vous ne pouvez pas faire des types dépendants, par exemple

trait Generic[A] {
  type Repr
  def to(value: A): Repr
  def from(value: Repr): A
}

import shapeless.Generic
def getRepr[A](value: A)(implicit gen: Generic[A]) =
  gen.to(value)

Voici le type retourné par to dépend du type d'entrée A (parce que l'implicite fournie dépend A):

case class Vec(x: Int, y: Int)
case class Rect(origin: Vec, size: Vec)
getRepr(Vec(1, 2))
// res1: shapeless.::[Int,shapeless.::[Int,shapeless.HNil]] = 1 :: 2 ::
     HNil
getRepr(Rect(Vec(0, 0), Vec(5, 5)))
// res2: shapeless.::[Vec,shapeless.::[Vec,shapeless.HNil]] = Vec(0,0)
     :: Vec(5,5) :: HNil

sans les membres de type ce serait impossible:

trait Generic2[A, Repr]
def getRepr2[A, R](value: A)(implicit generic: Generic2[A, R]): R =
  ???
  

Nous aurions dû passer la valeur désirée de Repr à getRepr comme   paramètre de type, effec Vely faisant getRepr inutile. Les cinq intui   take-away de cela est que les paramètres de type sont utiles comme « entrées » et   les membres de type sont utiles comme « sorties ».

S'il vous plaît voir le guide pour les détails.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top