311 votes

Abuser de l’algèbre des types de données algébriques - pourquoi cela fonctionne-t-il ?

Le "algébrique" l'expression pour les types de données algébriques semble très suggestif à quelqu'un avec une formation en mathématiques. Laissez-moi vous expliquer ce que je veux dire.

Après avoir défini les types de base

  • Produit
  • Union +
  • Singleton X
  • Unité 1

et à l'aide de l'abréviation X2 pour X•X et 2X pour X+X et cetera, on peut donc définir des expressions algébriques pour, par exemple, des listes liées

data List a = Nil | Cons a (List a)L = 1 + X • L

et des arbres binaires:

data Tree a = Nil | Branch a (Tree a) (Tree a)T = 1 + X • T2

Maintenant, mon premier instinct comme un mathématicien est le point de devenir fou avec ces expressions, et d'essayer de résoudre L et T. J'ai pu le faire par le biais répétée de substitution, mais il semble beaucoup plus facile d'abuser de la notation de terrifiant et de faire semblant que je peux la modifier à volonté. Par exemple, pour une liste liée:

L = 1 + X • L

(1 - X) • L = 1

L = 1 / (1 - X) = 1 + X + X2+ X3+ ...

où je l'ai utilisé la puissance de l'extension de la série de 1 / (1 - X) totalement injustifiée moyen de calculer un résultat intéressant, à savoir qu' L type est soit Nil, ou s'il contient 1 élément, ou s'il contient 2 éléments, ou 3, etc.

Il devient plus intéressant si nous le faisons pour les arbres binaires:

T = 1 + X • T2

X • T2- T + 1 = 0

T = (1 - sqrt(1 - 4 • X)) / (2 • X)

T = 1 + X + 2 • X2+ 5 • X3+ 14 • X4+ ...

encore une fois, en utilisant la puissance d'extension de la série (avec Wolfram Alpha). Cela exprime la non-évident (pour moi) le fait qu'il n'existe qu'un arbre binaire avec 1 élément, 2 arbres binaires avec deux éléments (le second élément peut être à gauche ou à droite direction), 5 arbres binaires avec trois éléments, etc.

Donc ma question est - ce que je fais ici? Ces opérations semblent injustifiées (ce qui est exactement la racine carrée d'un type de données algébrique de toute façon?) mais elles conduisent à des résultats sensées. le quotient de deux types de données algébriques ont aucune signification en informatique, ou est-il juste de notation de la tricherie?

Et, peut-être plus intéressant, est-il possible d'étendre ces idées? Est-il une théorie de l'algèbre de types qui permet, par exemple, des fonctions arbitraires sur les types, ou n'types de besoin d'une série de puissance de la représentation? Si vous pouvez définir une classe de fonctions, composition de fonctions ont pas de sens?

143voto

C. A. McCann Points 56834

Avertissement: Un grand nombre de ce qui ne fonctionne pas vraiment tout à fait raison lorsque vous ⊥, donc je vais flagrante ne pas tenir compte que, par souci de simplicité.

Quelques points de départ:

  • À noter que "l'union" n'est probablement pas le meilleur terme pour A+B ici--c'est précisément un disjoints de l'union de deux types, parce que les deux côtés sont distingués, même si leurs types sont les mêmes. Pour ce que ça vaut, le terme le plus commun est tout simplement "le type de somme".

  • Singleton types sont, en effet, tous les types d'unités. Ils se comportent à l'identique sous des manipulations algébriques et, plus important encore, la quantité d'information présente est encore préservée.

  • Vous voulez probablement un zéro type. Il n'y a pas un standard, mais le nom le plus courant est - Void. Il n'existe pas de valeurs dont le type est égal à zéro, tout comme il est une valeur dont le type est une.

Il reste encore une opération d'envergure qui manque ici, mais je vais y revenir dans un instant.

Comme vous l'avez probablement remarqué, Haskell a tendance à emprunter des concepts à partir de la Catégorie de la Théorie, et de tout ce qui précède a une manière très simple de l'interprétation en tant que telle:

  • Compte tenu des objets A et B dans Hask, leur produit A×B est l'unique (à isomorphisme) de type qui permet à deux projections fst : A×B → A et snd : A×B → B, où compte tenu de tout type C et les fonctions f : C → A, g : C → B, vous pouvez définir le couplage f &&& g : C → A×B tels que fst ∘ f &&& g) = f et de même pour g. Parametricity garantit les propriétés universelles automatiquement et mon moins que subtile choix de noms à vous donner l'idée. L' (&&&) opérateur est défini en Control.Arrow, par la manière.

  • Le double de ce qui précède est la coproduct A+B avec des injections inl : A → A+B et inr : B → A+B, où compte tenu de tout type C et les fonctions f : A → C, g : B → C, vous pouvez définir la copairing f ||| g : A+B → C telle que l'évidence des équivalences maintenez-la enfoncée. Encore une fois, parametricity garantit la plupart des parties difficiles automatiquement. Dans ce cas, la norme, les injections sont tout simplement Left et Right et le copairing est la fonction either.

La plupart des propriétés du produit et la somme types peuvent être dérivées à partir de ci-dessus. Notez que tout singleton est un terminal de Hask et tout type vide est un objet initial.

De retour à ladite manquant de l'opération, dans un cartésienne fermée catégorie que vous avez exponentielle des objets qui correspondent aux flèches de la catégorie. Nos flèches sont des fonctions, nos objets sont des types avec l'aimable *, et le type A -> B se comporte en effet comme BUn dans le contexte de la manipulation algébrique de types. Si ce n'est pas évident pourquoi cela devrait tenir, tenir compte du type Bool -> A. Avec seulement deux entrées, une fonction de ce type est isomorphe a ' deux valeurs de type A, c'est à dire (A, A). Pour Maybe Bool -> A nous avons trois entrées possibles, et ainsi de suite. Aussi, d'observer que, si nous reformuler la copairing définition ci-dessus pour utiliser la notation algébrique, on obtient l'identité de CA × CB = CA+B.

Comme pour pourquoi tout cela fait sens et, en particulier, pourquoi votre utilisation de la puissance d'extension de la série est justifiée: à noter qu'une grande partie de la ci-dessus se réfère à la "habitants" d'un type (c'est à dire, les valeurs distinctes du type) afin de démontrer le comportement algébrique. Pour faire de ce point de vue explicite:

  • Le type de produit (A, B) représente une valeur chacun de A et B, prises indépendamment. Ainsi, pour toute valeur fixe a :: A, il est une valeur de type (A, B) pour chaque habitant de B. C'est bien sûr le produit cartésien, et le nombre d'habitants du type de produit est le produit du nombre d'habitants de ces facteurs.

  • Le type de somme Either A B représente une valeur de A ou B, la droite et la gauche branches distingués. Comme mentionné plus tôt, c'est une union disjointe, et le nombre d'habitants de la somme est la somme du nombre d'habitants de la additionnées.

  • L'exponentielle de type B -> A représente une cartographie à partir des valeurs de type B pour les valeurs de type A. Pour toute fixe argument b :: B, toute valeur de A peut être assignée; une valeur de type B -> A choisit une telle cartographie pour chaque entrée, ce qui est équivalent à un produit de plusieurs copies de A comme B a habitants, d'où l'élévation à la puissance.

Alors que c'est tentant à première pour traiter les types de jeux, qui ne fonctionne pas très bien dans ce contexte, que nous avons disjoints de l'union plutôt que la norme de l'union des ensembles, il n'est pas évident interprétation de l'intersection ou de nombreuses autres opérations, et nous n'avons généralement pas de soins sur l'appartenance (en laissant que le type checker).

D'autre part, la construction ci-dessus passent beaucoup de temps à parler de comptage des habitants, et d'énumérer les valeurs possibles d'un type est un concept utile ici. Que rapidement nous conduit à la combinatoire énumérative, et si vous consultez liés article de Wikipédia, vous trouverez que l'une des premières choses qu'il n'est de définir des "paires" et "syndicats" exactement dans le même sens comme produit et somme types par voie de génération de fonctions, puis fait de même pour les "séquences" qui sont identiques à Haskell listes en utilisant exactement la même technique que vous avez fait.


Edit: Oh, et voici un petit bonus que je pense que démontre le point frappante. Vous avez mentionné dans un commentaire que pour un arbre de type T = 1 + T^2 vous pouvez dériver de l'identité T^6 = 1, ce qui est manifestement erroné. Toutefois, T^7 = T ne détiennent, et une bijection entre les arbres et les sept-n-uplets de l'arbre peut être construit directement, cf. Andreas Blass "Sept Arbres en Une seule".

Edit×2: Sur le sujet de la "dérivé d'un type de construction mentionné dans d'autres réponses, vous pouvez également profiter de ce document à partir du même auteur , qui s'appuie sur l'idée plus loin, y compris les notions de division et d'autres intéressantes, que sais-je encore.

49voto

sigfpe Points 5200

Arbres binaires sont définis par l'équation T=1+XT^2 dans le semi-anneau de types. Par construction, T=(1-sqrt(1-4X))/(2X) est défini par l'équation même dans le semi-anneau des nombres complexes. Donc, étant donné que nous sommes résoudre la même équation dans la même classe de structure algébrique, il en fait ne devrait pas être surprenant que de voir quelques similitudes.

Le hic, c'est que lorsque nous raisonnons sur des polynômes dans le semi-anneau des nombres complexes, nous utilisons généralement le fait que les nombres complexes forment un anneau ou même un champ de sorte que nous nous trouvons à l'aide des opérations telles que la soustraction qui ne s'appliquent pas à semi-anneaux. Mais nous permet souvent d'éliminer les soustractions de nos arguments si nous avons une règle qui nous permet d'annuler des deux côtés de l'équation. C'est le genre de chose prouvée par Fiore et le Leinster en montrant que de nombreux arguments sur les anneaux peuvent être transférées à des semi-anneaux.

Cela signifie que beaucoup de vos connaissances mathématiques sur les anneaux peuvent être transférés de manière fiable à des types. Comme un résultat, certains arguments impliquant des nombres complexes ou d'alimentation de la série (dans l'anneau de pouvoir formel de la série) peut porter sur des types complètement rigoureuses.

Cependant, il ya plus à l'histoire que ce. C'est une chose à prouver deux types sont égaux (dire) en montrant deux séries de puissances sont égales. Mais vous pouvez également déduire de l'information sur les types en inspectant les termes de la puissance de la série. Je ne suis pas sûr de ce que la déclaration officielle ici devrait être. (Je recommande de Brent Yorgey de papier sur la combinatoire des espèces pour certains travaux qui sont étroitement liés, mais les espèces sont pas les mêmes que les types.)

Ce que je trouve tout à fait hallucinant, c'est que ce que vous avez découvert peut être étendu au calcul différentiel et intégral. Les théorèmes sur le calcul peut être transféré sur le semi-anneau de types. En fait, même les arguments sur les différences finies peuvent être transférées et vous trouvez que la musique classique théorèmes de l'analyse numérique ont des interprétations en théorie des types.

Amusez-vous!

23voto

newacct Points 42530

Il semble que tout ce que tu fais est en expansion de la relation de récurrence.

Et puisque les règles pour les opérations sur les types de travaux comme les règles pour les opérations arithmétiques, vous pouvez utiliser des moyens algébriques pour aider à vous trouver un moyen d’étendre la relation de récurrence (puisqu’il n’est pas évident).

18voto

yatima2975 Points 4191

Je n'ai pas de réponse complète, mais ces manipulations ont tendance à "juste travail". Un document pertinent pourrait être les Objets des Catégories comme des Nombres Complexes par Fiore et le Leinster - je suis venu à travers ce que l'un lors de la lecture de sigfpe blog sur un sujet connexe ; le reste de ce blog est une mine d'or pour des idées similaires et vaut le détour!

Vous pouvez également différencier les types de données, par la voie qui vous permettra d'obtenir approprié fermeture à Glissière pour le type de données!

10voto

L'Algèbre de Processus de communications (ACP) traite avec les mêmes types d'expressions de processus. Il vous propose l'addition et la multiplication comme des opérateurs pour le choix et la séquence, associés à des éléments neutres. Sur la base de ces il y a des opérateurs pour les autres constructions, telles que le parallélisme et la perturbation. Voir http://en.wikipedia.org/wiki/Algebra_of_Communicating_Processes. Il y a aussi un document en ligne nommé "Une Brève Histoire de l'Algèbre de Processus".

Je suis en train de travailler sur l'extension des langages de programmation avec les pays ACP. En avril dernier, j'ai présenté un document de recherche à la Scala Jours 2012, disponible à http://code.google.com/p/subscript/

Lors de la conférence, j'ai fait preuve d'un débogueur en cours d'exécution en parallèle récursive spécification d'un sac:

Sac = A; (Sac&a)

où l'Une et de un stand pour l'entrée et la sortie des actions; le point-virgule et esperluette stand pour la séquence et le parallélisme. Voir la vidéo à SkillsMatter, accessible à partir du lien précédent.

Un sac de spécification plus comparable à

L = 1 + X•L

serait

B = 1 + X ET B

ACP définit le parallélisme en termes de choix et de séquence à l'aide des axiomes; voir l'article de Wikipedia. Je me demande ce que le sac de l'analogie pour

L = 1 / (1-X)

ACP style de programmation est très pratique pour les analyseurs de texte et de l'interface graphique contrôleurs. Spécifications telles que

searchCommand = cliqué(searchButton) + touche(Enter)

cancelCommand = cliqué(cancelButton) + touche(Echap)

peut être écrit de façon plus concise, en faisant les deux améliorations "cliqué" et "clé" implicite (comme ce que Scala permet avec des fonctions). Par conséquent, nous pouvons écrire:

searchCommand = searchButton + Entrée

cancelCommand = cancelButton + Escape

La droite, contient maintenant des opérandes qui sont données, plutôt que sur les processus. À ce niveau, il n'est pas nécessaire besoin de savoir à quoi implicite améliorations vont transformer ces opérandes dans les processus; ils ne seraient pas nécessairement affiner en entrée d'actions; actions de sortie serait également s'appliquer, par exemple, dans la spécification d'un test de robot.

Les processus d'obtenir de cette façon les données sont des compagnons; j'ai donc inventé le terme "élément de l'algèbre".

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