Quand je veux lire sur la programmation logique, je tombe toujours sur deux façons "principales" de le faire de nos jours:
Ce qui m'intéresse maintenant: quelles sont les principales différences techniques entre les deux? Sont-ils très similaires dans leur approche et leur mise en œuvre, ou adoptent-ils des approches complètement différentes de la programmation logique? De quelles branches des mathématiques proviennent-ils et quels sont les fondements théoriques?
Tout d’abord, permettez-moi de vous féliciter pour votre belle icône pw0n1e.
Il est difficile de répondre à cette question, en grande partie parce qu’il existe de nombreuses variantes de miniKanren et de Prolog. miniKanren et Prolog sont en réalité des familles de langues, ce qui rend difficile la comparaison de leurs fonctionnalités, voire de la manière dont elles sont utilisées dans la pratique. Pour cette raison, prenez tout ce que je vais vous dire avec prudence: si je dis que Prolog utilise la recherche en profondeur d'abord, sachez que de nombreuses implémentations de Prolog prennent en charge d'autres stratégies de recherche et que d'autres stratégies de recherche peuvent également être encodées à la méta. niveau interprète. Néanmoins, miniKanren et Prolog ont des philosophies de conception différentes et font des compromis différents.
Prolog est l'un des deux langages classiques de la programmation de l'intelligence artificielle symbolique (l'autre langage classique étant LISP). Prolog excelle dans la mise en œuvre de systèmes symboliques basés sur des règles dans lesquels les connaissances déclaratives sont codées dans une logique de premier ordre. Le langage est optimisé pour l'expressivité et l'efficacité de ces types d'applications, parfois au détriment de la pureté logique. Par exemple, par défaut, Prolog n'utilise pas la "vérification d'occurrence" dans l'unification. D'un point de vue mathématique/logique, cette version de l'unification est incorrecte. Cependant, la vérification des événements est coûteuse et, dans la plupart des cas, l'absence de vérification des événements n'est pas un problème. C’est une décision de conception très pragmatique, tout comme l’utilisation par Prolog de la recherche en profondeur d’abord et de la coupe (!
) pour contrôler les retours en arrière. Je suis sûr que ces décisions étaient absolument nécessaires lors de l'utilisation du matériel informatique des années 1970 et sont aujourd'hui très utiles pour traiter de gros problèmes et pour traiter des espaces de recherche énormes (souvent infinis!).
Prolog prend en charge de nombreuses fonctionnalités "extra logiques" ou "non logiques", notamment cut, assert
et retract
, la projection de variables pour l'arithmétique à l'aide de is
, etc. Bon nombre de ces fonctionnalités facilitent l'expression de flux de contrôle complexes et la manipulation de la base de données globale de faits de Prolog. Une caractéristique très intéressante de Prolog est que le code Prolog est lui-même stocké dans la base de données globale des faits et qu’il peut être interrogé au moment de l’exécution. Il est donc trivial d’écrire des méta-interprètes qui modifient le comportement du code Prolog sous interprétation. Par exemple, il est possible d’encoder la recherche en largeur dans Prolog à l’aide d’un méta-interprète qui modifie l’ordre de recherche. C'est une technique extrêmement puissante qui n'est pas bien connue en dehors du monde Prolog. 'The Art of Prolog' décrit cette technique en détail.
Des efforts considérables ont été déployés pour améliorer les implémentations de Prolog, dont la plupart sont basées sur la Warren Abstract Machine (WAM). Le modèle WAM utilise un modèle à effets secondaires dans lequel les valeurs sont attribuées de manière destructive aux variables logiques, ces effets étant annulés lors du retour en arrière. De nombreuses fonctionnalités peuvent être ajoutées à Prolog en étendant les instructions du WAM. L’un des inconvénients de cette approche est qu’il peut être difficile de lire les documents de mise en œuvre de Prolog sans une compréhension solide du WAM. D'autre part, les développeurs de Prolog ont un modèle commun pour discuter des problèmes de mise en œuvre. Parallèlement à Prolog, de nombreuses recherches ont abouti à Andorra Prolog dans les années 1990. Au moins certaines de ces idées sont conservées dans Ciao Prolog. (Ciao Prolog regorge d'idées intéressantes, dont beaucoup vont bien au-delà de la norme Prolog.)
Prolog a une belle syntaxe de style "correspondance de motif" qui donne des programmes très succincts. Les Prologers aiment leur syntaxe, tout comme les Lispers aiment leurs expressions-s. Prolog a aussi une grande bibliothèque de prédicats standards. En raison de toute l’ingénierie nécessaire pour rendre le WAM rapide, il existe des implémentations très performantes et matures de Prolog. En conséquence, de nombreux grands systèmes basés sur la connaissance ont été entièrement écrits dans Prolog.
miniKanren a été conçu comme un langage de programmation logique minimal, avec une implémentation petite, facilement compréhensible et facilement piratable. miniKanren a été initialement intégré à Scheme et a été porté dans des dizaines d'autres langues hôtes au cours de la dernière décennie. L'implémentation miniKanren la plus répandue est "core.logic" dans Clojure, qui dispose désormais de nombreuses extensions de type Prolog et de nombreuses optimisations. Récemment, le cœur de la mise en œuvre de miniKanren a encore été simplifié, donnant ainsi naissance à un minuscule "micro-noyau" appelé "microKanren". miniKanren peut ensuite être implémenté sur ce noyau microKanren. Porter microKanren ou miniKanren dans un nouveau langage hôte est devenu un exercice standard pour les programmeurs apprenant miniKanren. En conséquence, les langages de haut niveau les plus populaires ont au moins une implémentation miniKanren ou microKanren.
Les implémentations standard de miniKanren et microKanren ne contiennent aucune mutation ni aucun autre effet secondaire, à une seule exception près: certaines versions de miniKanren utilisent une égalité de pointeur pour comparer les variables logiques. Je considère cela comme un "effet bénin", bien que de nombreuses implémentations évitent même cet effet en passant un compteur à travers l'implémentation. Il n'y a pas non plus de base de données globale. La philosophie de mise en œuvre de miniKanren est inspirée par la programmation fonctionnelle: les mutations et les effets doivent être évités, et toutes les constructions linguistiques doivent respecter la portée lexicale. Si vous regardez attentivement la mise en œuvre, vous remarquerez peut-être même quelques monades. L'implémentation de la recherche est basée sur la combinaison et la manipulation de flux paresseux, encore une fois sans utiliser de mutation. Ces choix d’implémentation conduisent à des compromis très différents de ceux de Prolog. Dans Prolog, la recherche de variable est une durée constante, mais le retour en arrière nécessite l'annulation des effets secondaires. Dans miniKanren, la recherche de variable est plus chère, mais le retour en arrière est "gratuit". En fait, il n'y a pas de retour en arrière dans miniKanren, en raison de la manière dont les flux sont gérés.
Un aspect intéressant de l'implémentation de miniKanren est que le code est intrinsèquement thread-safe et, du moins en théorie, trivialement parallélisable. Bien sûr, paralléliser le code sans le ralentir n’est pas trivial, étant donné que chaque fil ou processus doit recevoir suffisamment de travail pour compenser la surcharge de parallélisation. Néanmoins, il s’agit d’un domaine d’application de miniKanren qui, je l’espère, bénéficiera de plus d’attention et d’expérimentation.
miniKanren utilise la vérification d'occurrence pour l'unification et utilise une recherche d'entrelacement complète au lieu d'une recherche en profondeur. La recherche entrelacée utilise plus de mémoire que la recherche en profondeur d'abord, mais peut trouver des réponses dans certains cas dans lesquels la recherche en profondeur d'abord diverge/boucle en permanence. miniKanren prend en charge quelques opérateurs extra-logiques ---conda
, condu
et project
, par exemple. conda
et condu
peuvent être utilisés pour simuler la coupe de Prolog, et project
peut être utilisé pour obtenir la valeur associée à une variable logique.
La présence de conda
, condu
et project
--- ainsi que la possibilité de modifier facilement la stratégie de recherche --- permet aux programmeurs d'utiliser miniKanren comme un Prolog intégré la langue. Cela est particulièrement vrai pour les utilisateurs de "core.logic" de Clojure, qui inclut de nombreuses extensions de type Prolog. Cette utilisation "pragmatique" de miniKanren semble représenter la majorité des utilisations de miniKanren dans l'industrie. Les programmeurs souhaitant ajouter un système de raisonnement basé sur la connaissance à une application existante écrite en Clojure ou Python ou JavaScript ne sont généralement pas intéressés par la réécriture de l’ensemble de leur application dans Prolog. Intégrer un petit langage de programmation logique dans Clojure ou Python est beaucoup plus attrayant. Une implémentation intégrée de Prolog fonctionnerait tout aussi bien à cet effet, vraisemblablement. Je suppose que miniKanren est devenu populaire en tant que langage logique embarqué en raison de son noyau minuscule et pur mise en œuvre, ainsi que les exposés, les billets de blog, les tutoriels et autres supports pédagogiques publiés depuis la publication de "The Reasoned Schemer".
En plus de l'utilisation de miniKanren en tant que langage de programmation pragmatique à logique intégrée, similaire à celui de Prolog, miniKanren est utilisé pour la recherche en programmation "relationnelle". C'est-à-dire, en écrivant des programmes qui se comportent comme des relations mathématiques plutôt que des fonctions mathématiques. Par exemple, dans Scheme, la fonction append
peut ajouter deux listes, renvoyant une nouvelle liste: l'appel de fonction (append '(a b c) '(d e))
retourne la liste (a b c d e)
. Cependant, nous pouvons aussi traiter append
comme une relation à trois positions plutôt que comme une fonction à deux arguments. L'appel (appendo '(a b c) '(d e) Z)
associerait alors la variable logique Z
à la liste (a b c d e)
. Bien sûr, les choses deviennent plus intéressantes lorsque nous plaçons des variables logiques dans d'autres positions. L'appel (appendo X '(d e) '(a b c d e))
associe X
à (a b c)
, tandis que l'appel (appendo X Y '(a b c d e))
associe X
et Y
à des paires de listes qui, une fois ajoutées, sont égales à (a b c d e)
. Par exemple, X
= (a b)
et Y
= (c d e)
sont une de ces paires de valeurs. On peut aussi écrire (appendo X Y Z)
, qui produira une infinité de triples de listes X
, Y
et Z
de telle sorte que l’ajout de X
à Y
produira Z
.
Cette version relationnelle de append
peut être facilement exprimée dans Prolog et est en fait illustrée dans de nombreux tutoriels Prolog. En pratique, les programmes Prolog plus complexes ont tendance à utiliser au moins quelques fonctionnalités extra-logiques, telles que cut, qui empêchent de traiter le programme résultant comme une relation. En revanche, miniKanren est explicitement conçu pour prendre en charge ce style de programmation relationnelle. Des versions plus récentes de miniKanren prennent en charge la résolution de contraintes symboliques (symbolo
, numbero
, absento
, contraintes d'inégalité, programmation logique nominale) pour faciliter la rédaction de programmes non triviaux. en tant que relations. En pratique, je n'utilise jamais les fonctionnalités extra-logiques de miniKanren et j'écris tous mes programmes miniKanren en tant que relations. Les programmes relationnels les plus intéressants sont les interpréteurs relationnels d'un sous-ensemble de Scheme. Ces interprètes ont de nombreuses capacités intéressantes, telles que générer un million de programmes Scheme qui correspondent à la liste (I love you)
, ou générant trivialement des quines (programmes qui s’évaluent eux-mêmes).
miniKanren fait un certain nombre de compromis pour permettre ce style de programmation relationnel, qui est très différent des compromis que Prolog fait. Au fil du temps, miniKanren a ajouté de nouvelles contraintes symboliques, devenant réellement un langage de programmation Constraint Logic à orientation symbolique. Dans de nombreux cas, ces contraintes symboliques permettent d'éviter l'utilisation d'opérateurs extra-logiques tels que condu
et project
. Dans d'autres cas, ces contraintes symboliques ne sont pas suffisantes. L'un des domaines actifs de la recherche sur miniKanren est de mieux prendre en charge les contraintes symboliques, ainsi que la question plus large de savoir comment écrire des programmes plus vastes et plus complexes comme relations.
En bref, miniKanren et Prolog ont des fonctionnalités, des implémentations et des utilisations intéressantes, et je pense que cela vaut la peine d'apprendre les idées des deux langues. Il existe également d'autres langages de programmation logique très intéressants, tels que Mercury, Curry et Gödel, qui ont chacun leur propre point de vue sur la programmation logique.
Je terminerai avec quelques ressources miniKanren:
Le site Web principal de miniKanren: http://minikanren.org/
Une interview que j'ai donnée sur la programmation relationnelle et miniKanren, y compris une comparaison avec Prolog: http://www.infoq.com/interviews/byrd-relational-programming-minikanren
À votre santé,
--Volonté
Réponse provisoire:
D'après les informations dont je dispose, "The Reasoned Schemer" a introduit la programmation logique de base dans une syntaxe et un style de programmation fonctionnels, en ajoutant notamment les objectifs constants "#u" (fail) et "#s" (suceeed) aux valeurs booléennes "#t "et" #f ". Il utilisait la même approche de la programmation logique que Prolog: l’unification et la recherche en arrière. Je verrai si j'ai le temps de récupérer ce livre de mon étagère au cours du week-end. La branche des mathématiques est une logique de premier ordre sous forme restreinte, dans ce cas des clauses de Horn et la résolution de résolution. Voir: Logique informatique: mémoires du passé et défis pour l'avenir par John Alan Robinson et Les premières années de la programmation logique par Robert Kowalski pour un démarrage à froid.