42 votes

Pourquoi la technique Aux est-elle requise pour les calculs de type?

Je suis assez sûr que je suis absent quelque chose ici, car je suis assez nouveau à l'Informe et je suis en train d'apprendre, mais quand c'est l'Auxiliaire technique réellement nécessaire? Je vois qu'il est utilisé pour exposer un type déclaration en la soulevant jusqu'à la signature d'un autre "compagnon" type définition.

trait F[A] { type R; def value: R }
object F { type Aux[A,RR] = F[A] { type R = RR } }

mais n'est-ce pas à peu près équivalent à juste mettre R dans la signature d'un type de F ?

trait F[A,R] { def value: R }
implicit def fint = new F[Int,Long] { val value = 1L }
implicit def ffloat = new F[Float,Double] { val value = 2.0D }
def f[T,R](t:T)(implicit f: F[T,R]): R = f.value
f(100)    // res4: Long = 1L
f(100.0f) // res5: Double = 2.0

Je vois que le chemin-dépendants de type aurait des effets bénéfiques si l'on pouvait les utiliser dans des listes d'arguments, mais nous savons que nous ne pouvons pas faire

def g[T](t:T)(implicit f: F[T], r: Blah[f.R]) ...

ainsi, nous sommes encore obligés de mettre un autre type de paramètre dans la signature de l' g. En utilisant l' Aux technique, nous sommes également tenus de passer plus de temps à écrire le compagnon object. À partir d'un point de vue de l'utilisation, il ressemblerait à un utilisateur naïf comme moi qu'il n'y a pas d'avantage à utiliser le chemin d'accès des types dépendants à tous.

Il n'y a qu'un seul cas que je pense, c'est, pour un type donné de niveau calcul de plus d'un type de niveau de résultat est retourné, et vous souhaiterez peut-être utiliser un seul d'entre eux.

Je suppose que tout cela se résume à me donnant sur quelque chose dans mon exemple simple.

54voto

Travis Brown Points 56342

Il y a deux questions distinctes ici:

  1. Pourquoi n'Informe du type d'utilisation des membres au lieu des paramètres de type, dans certains cas, dans certaines classes de type?
  2. Pourquoi n'Informe comprennent Aux type d'alias dans le compagnon des objets de ces classes de type?

Je vais commencer par la deuxième question parce que la réponse est plus simple: l' Aux type d'alias sont entièrement syntaxique, commodité. Vous ne jamais avoir à les utiliser. Par exemple, supposons que nous voulions écrire une méthode qui vous permettra de compilation uniquement lorsqu'elle est appelée avec deux les hlist qui ont la même longueur:

import shapeless._, ops.hlist.Length

def sameLength[A <: HList, B <: HList, N <: Nat](a: A, b: B)(implicit
  al: Length.Aux[A, N],
  bl: Length.Aux[B, N]
) = ()

L' Length type classe a un paramètre de type (pour l' HList type) et un type de membre (pour l' Nat). L' Length.Aux de la syntaxe, il est relativement facile de se référer à l' Nat type de membre dans le paramètre implicite de la liste, mais c'est juste une commodité-ce qui suit est exactement l'équivalent:

def sameLength[A <: HList, B <: HList, N <: Nat](a: A, b: B)(implicit
  al: Length[A] { type Out = N },
  bl: Length[B] { type Out = N }
) = ()

L' Aux version a quelques avantages par rapport à l'écriture du type améliorations dans ce sens: c'est moins bruyant, et il ne nécessite pas de nous rappeler le nom de la membre de type. Ces derniers sont purement ergonomique questions, même si-l' Aux des alias de rendre notre code plus facile à lire et à écrire, mais ils ne changent pas ce que nous pouvons ou ne pouvons pas faire avec le code de façon significative.

La réponse à la première question est un peu plus complexe. Dans de nombreux cas, y compris mon sameLength, il n'y a aucun avantage à en Out être un membre de type au lieu d'un paramètre de type. Parce que Scala ne pas permettre à plusieurs paramètre implicite sections, nous avons besoin d' N être un paramètre de type pour notre méthode, si nous voulons vérifier que les deux Length des cas ont le même Out type. À ce stade, l' Out sur Length pourrait aussi bien être un paramètre de type (au moins de notre point de vue que les auteurs de l' sameLength).

Dans d'autres cas, cependant, nous pouvons tirer avantage du fait que Informes parfois (je vais en parler plus précisément dans un instant) utilise le type de membres au lieu des paramètres de type. Par exemple, supposons que nous voulions écrire une méthode qui retourne une fonction qui vous permet de convertir un cas spécifié le type de classe en HList:

def converter[A](implicit gen: Generic[A]): A => gen.Repr = a => gen.to(a)

Maintenant, nous pouvons l'utiliser comme ceci:

case class Foo(i: Int, s: String)

val fooToHList = converter[Foo]

Et nous allons avoir une belle Foo => Int :: String :: HNil. Si Generics' Repr ont un type de paramètre à la place d'un membre de type, il faudrait écrire quelque chose comme ceci à la place:

// Doesn't compile
def converter[A, R](implicit gen: Generic[A, R]): A => R = a => gen.to(a)

Scala ne prend pas en charge partielle de l'application de paramètres de type, de sorte que chaque fois que nous appelons le présent (hypothétique) de la méthode que nous aurions à spécifier à la fois les paramètres de type puisque nous voulons spécifier A:

val fooToHList = converter[Foo, Int :: String :: HNil]

De ce fait, il fondamentalement sans valeur, puisque le but était de permettre au générique de machines de comprendre la représentation.

En général, quand un type est déterminée de manière unique par un type de la classe d'autres paramètres, Informe permettra d'en faire un membre de type au lieu d'un paramètre de type. Tous les cas de classe a qu'une seule représentation générique, de sorte Generic a un paramètre de type (pour le cas type de classe) et un type de membre (pour le type de représentation); chaque HList a une seule longueur, Length a un paramètre de type et un type de membre, etc.

Faire unique qui a déterminé les types de type de membres au lieu des paramètres de type signifie que si nous voulons les utiliser uniquement comme des chemin des types dépendants (comme dans le premier converter ci-dessus), nous pouvons, mais si nous voulons les utiliser comme si elles étaient les paramètres de type, nous pouvons toujours écrire soit le type de raffinement (ou la plus agréable du point de vue syntaxique Aux version). Si Informes fait ces types de paramètres de type dès le début, il ne serait pas possible d'aller dans la direction opposée.

Comme une note de côté, cette relation entre un type de classe de type "paramètres" (j'utilise des guillemets car ils ne peuvent être des paramètres dans le littéral Scala sens) est appelée une "dépendance fonctionnelle" dans des langages comme Haskell, mais vous ne devriez pas sentir comme vous avez besoin de comprendre quoi que ce soit sur les dépendances fonctionnelles en Haskell pour obtenir ce qu'il se passe en Informe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X