Ce sont de type lambdas en Scala et quels sont leurs avantages?
Parfois je tombe dans le semi-mystérieux notation de
def f[T](..) = new T[({type l[A]=SomeType[A,..]})#l] {..}
en Scala de messages de blog, qui lui ont donné un "nous avons utilisé ce type lambda truc" handwave.
Alors que j'ai quelques intutition sur ce sujet (nous gagnons un anonyme, un paramètre de type A
sans avoir à polluer la définition?), J'ai pas de source évidente à décrire ce que le type lambda truc est, et quels sont ses avantages. Est-il juste de sucre syntaxique, ou faut-il ouvrir quelques nouvelles dimensions?
- Voir également.
Vous devez vous connecter pour publier un commentaire.
Type lambdas sont essentiels un peu de temps lorsque vous travaillez avec plus de kinded types.
Considérons un exemple simple de définir une monade pour le droit de projection Soit[A, B]. La monade typeclass ressemble à ceci:
Maintenant, est un type de constructeur à deux arguments, mais de mettre en œuvre Monade, vous avez besoin de lui donner un constructeur de type d'un argument. La solution pour cela est d'utiliser un type lambda:
Ceci est un exemple de nourrissage dans le type de système que vous avez au curry le type de, tel que, lorsque vous souhaitez créer une instance de EitherMonad, vous devez spécifier l'un des types, l'autre de cours est fourni au moment de l'appel du point ou de la lier.
Le type lambda truc exploite le fait qu'un bloc vide dans une position crée un anonyme, un type de structure. Nous utilisons ensuite la # syntaxe pour obtenir un type de membre.
Dans certains cas, vous pouvez avoir besoin de plus sophistiqué de type lambdas qui sont une douleur à écrire sur la ligne. Voici un exemple de mon code à partir d'aujourd'hui:
Cette classe existe exclusivement afin que je puisse utiliser un nom comme FG[F, G]#IterateeM pour désigner le type de la IterateeT monade spécialisé pour certains transformateur version d'un deuxième monade qui est spécialisé pour la troisième monade. Lorsque vous commencez à pile, ces sortes de constructions devenu très nécessaire. Je n'ai jamais instancier un FG, bien sûr; il est juste là comme un hack de me laisser exprimer ce que je veux dans le système de type.
bind
méthode pour votreEitherMonad
classe. 🙂 A part ça, si j'ai peut-canal Adriaan pour un deuxième ici, vous ne l'utilisez pas plus-kinded types de cet exemple. Vous êtes dansFG
, mais pas dansEitherMonad
. Au contraire, vous êtes à l'aide de type de constructeurs, qui ont en quelque sorte* => *
. Ce type est de l'ordre de-1, ce qui n'est pas "supérieur".*
a été de l'ordre-1, mais en tout cas Monade a en quelque sorte(* => *) => *
. Aussi, vous remarquerez que j'ai précisé "le droit de la projection deEither[A, B]
" - la mise en œuvre est trivial (mais un bon exercice si vous ne l'avez pas fait avant!)*=>*
plus élevé est justifié par l'analogie que nous n'avons pas appeler une fonction ordinaire (que des cartes non des fonctions non fonctions, en d'autres termes, la plaine des valeurs à la plaine de valeurs) d'ordre supérieur de la fonction.Type expressions with kinds like (*⇒*)⇒* are called higher-order typeoperators.
Les avantages sont exactement les mêmes que ceux qui sont conférés par les fonctions anonymes.
Un exemple d'utilisation, avec Scalaz 7. Nous voulons utiliser un
Functor
qui permet de cartographier une fonction sur le deuxième élément dans unTuple2
.Scalaz fournit quelques conversions implicites qui peuvent déduire le type de l'argument de
Functor
, nous avons donc souvent éviter d'écrire ces lignes. La ligne précédente peut être réécrite comme:Si vous utilisez l'Ide, vous pouvez activer les Paramètres, le Code de Style, la Scala, le Pliage, le Type Lambda. Ensuite, cela cache la crufty parties de la syntaxe, et présente la plus agréable au goût:
Une future version de Scala peut directement à l'appui d'une telle syntaxe.
(1, 2).map(a => a + 1)
dans REPL: ` <console>:11: erreur: la valeur de la carte n'est pas membre de l' (Int, Int) (1, 2).carte(a => un + 1) ^`De mettre les choses dans leur contexte: Cette réponse a été posté dans un autre thread. Vous êtes le voir ici, car les deux fils ont été fusionnés. La question de l'instruction dans le dit fil a été comme suit:
Réponse:
Un trait de soulignement dans les cases après
P
implique que c'est un type constructeur prend un type et retourne un autre type. Exemples de type de constructeurs avec ce genre:List
,Option
.Donner
List
unInt
, un type de béton, et il vous donneList[Int]
, un autre type de béton. DonnerList
unString
et il vous donneList[String]
. Etc.Donc,
List
,Option
peut être considéré comme le type de fonctions d'arité 1. Officiellement nous dire, qu'ils ont une sorte* -> *
. L'astérisque indique un type de.Maintenant
Tuple2[_, _]
est un constructeur de type avec l'aimable(*, *) -> *
c'est à dire vous avez besoin de lui donner deux types d'obtenir un nouveau type.Depuis leurs signatures ne correspondent pas, vous ne pouvez pas remplacer
Tuple2
pourP
. Ce que vous devez faire est de appliquer partiellementTuple2
sur l'un de ses arguments, ce qui va nous donner un constructeur de type avec l'aimable* -> *
, et nous pouvons substituer pourP
.Malheureusement Scala n'a pas de syntaxe particulière pour l'application partielle de type constructeurs, et ainsi nous avons du avoir recours à la monstruosité du type lambda. (Ce que vous avez dans votre exemple.) Ils sont appelés parce qu'ils sont analogues aux expressions lambda qui existent au plan de la valeur.
L'exemple suivant qui pourraient vous aider:
Edit:
Plus de valeur niveau et le type de niveau de parallels.
Dans le cas où vous avez présenté, le paramètre de type
R
est locale à la fonctionTuple2Pure
et donc vous ne pouvez pas simplement définirtype PartialTuple2[A] = Tuple2[R, A]
, car il n'y a tout simplement pas de place où vous pouvez mettre ce synonyme.À faire face à de tels cas, j'utilise l'astuce suivante qui rend l'utilisation de type de membres. (J'espère que l'exemple est auto-explicatif.)
type World[M[_]] = M[Int]
causes que tout ce que nous mettons enA
dansX[A]
laimplicitly[X[A] =:= Foo[String,Int]]
est toujours vrai, je suppose.