Je suis développeur Web depuis un certain temps maintenant et j'ai récemment commencé à apprendre de la programmation fonctionnelle. Comme d'autres, j'ai eu beaucoup de mal à appliquer bon nombre de ces concepts à mon travail professionnel. Pour moi, la principale raison en est que je vois un conflit entre l'objectif de FP de rester apatride semble en contradiction avec le fait que la plupart des travaux de développement Web que j'ai effectués ont été fortement liés aux bases de données, qui sont très centrées sur les données.
Une chose qui a fait de moi un développeur beaucoup plus productif du côté OOP des choses a été la découverte de mappeurs objet-relationnels comme MyGeneration d00dads pour .Net, Class :: DBI pour Perl, ActiveRecord pour Ruby , etc. Cela m'a permis de ne pas écrire des instructions d'insertion et de sélection toute la journée et de me concentrer sur l'utilisation des données facilement en tant qu'objets. Bien sûr, je pouvais toujours écrire des requêtes SQL lorsque leur puissance était nécessaire, mais sinon elles étaient abstraites joliment dans les coulisses.
Maintenant, en ce qui concerne la programmation fonctionnelle, il semble que la plupart des frameworks Web FP comme Links nécessitent d'écrire beaucoup de code SQL standard, comme dans cet exemple . Weblocks semble un peu mieux, mais il semble utiliser une sorte de modèle OOP pour travailler avec les données, et nécessite toujours que le code soit écrit manuellement pour chaque table de votre base de données comme dans cet exemple . Je suppose que vous utilisez une génération de code pour écrire ces fonctions de mappage, mais cela semble décidément non-LISP.
(Notez que je n'ai pas regardé les Weblocks ou les liens de très près, je peux juste mal comprendre comment ils sont utilisés).
Donc, la question est, pour les parties d'accès à la base de données (qui je crois sont assez grandes) de l'application Web, ou tout autre développement nécessitant une interface avec une base de données SQL, nous semblons être obligés de suivre l'un des chemins suivants:
De toute évidence, aucune de ces options ne semble idéale. A trouvé un moyen de contourner ces problèmes? Y a-t-il vraiment un problème, même ici?
Remarque: Personnellement, je connais bien LISP sur le front FP, donc si vous voulez donner des exemples et connaître plusieurs langues FP, LISP serait probablement la langue préférée de choix
PS: Pour les problèmes spécifiques à d'autres aspects du développement Web, voir cette question .
Tout d'abord, je ne dirais pas que CLOS (Common LISP Object System) est "pseudo-OO". C'est de première classe OO.
Deuxièmement, je crois que vous devez utiliser le paradigme qui correspond à vos besoins.
Vous ne pouvez pas stocker de données sans état, alors qu'une fonction est un flux de données et n'a pas vraiment besoin d'un état.
Si vous avez plusieurs besoins mélangés, mélangez vos paradigmes. Ne vous limitez pas à n'utiliser que le coin inférieur droit de votre boîte à outils.
En venant à ce point de vue d'une personne de base de données, je trouve que les développeurs front-end essaient trop de trouver des moyens d'adapter les bases de données à leur modèle plutôt que de considérer les moyens les plus efficaces d'utiliser des bases de données qui ne sont pas orientées objet ou fonctionnelles mais relationnelles et utilisant théorie des ensembles. J'ai vu que cela se traduisait généralement par un code peu performant. De plus, il crée du code difficile à régler.
Lors de l'examen de l'accès à la base de données, il y a trois considérations principales: l'intégrité des données (pourquoi toutes les règles métier doivent être appliquées au niveau de la base de données et non via l'interface utilisateur), les performances et la sécurité. SQL est écrit pour gérer les deux premières considérations plus efficacement que n'importe quel langage frontal. Parce qu'il est spécialement conçu pour cela. La tâche d'une base de données est très différente de celle d'une interface utilisateur. Faut-il s'étonner que le type de code le plus efficace pour gérer la tâche soit conceptuellement différent?
Et les bases de données contiennent des informations essentielles à la survie d'une entreprise. Il n'est pas étonnant que les entreprises ne soient pas disposées à expérimenter de nouvelles méthodes lorsque leur survie est en jeu. De nombreuses entreprises ne veulent même pas passer à de nouvelles versions de leur base de données existante. Il existe donc un conservatisme inhérent à la conception des bases de données. Et c'est délibérément de cette façon.
Je n'essaierais pas d'écrire T-SQL ou d'utiliser des concepts de conception de base de données pour créer votre interface utilisateur, pourquoi voudriez-vous utiliser votre langage d'interface et vos concepts de conception pour accéder à ma base de données? Parce que vous pensez que SQL n'est pas assez sophistiqué (ou nouveau)? Ou vous ne vous sentez pas à l'aise avec ça? Ce n'est pas parce que quelque chose ne correspond pas au modèle avec lequel vous vous sentez le plus à l'aise qu'il est mauvais ou mauvais. Cela signifie qu'il est différent et probablement différent pour une raison légitime. Vous utilisez un outil différent pour une tâche différente.
Vous devriez consulter le document "Out of the Tar Pit" de Ben Moseley et Peter Marks, disponible ici: "Out of the Tar Pit" (6 février 2006)
C'est un classique moderne qui détaille un paradigme/système de programmation appelé Programmation relationnelle fonctionnelle. Sans être directement lié aux bases de données, il explique comment isoler les interactions avec le monde extérieur (bases de données, par exemple) du noyau fonctionnel d'un système.
Le papier discute également comment implémenter un système où l'état interne de l'application est défini et modifié en utilisant une algèbre relationnelle, qui est évidemment liée aux bases de données relationnelles.
Ce document ne donnera pas une réponse exacte sur la façon d'intégrer les bases de données et la programmation fonctionnelle, mais il vous aidera à concevoir un système pour minimiser le problème.
Les langages fonctionnels n'ont pas pour objectif de rester apatrides, ils ont pour objectif de rendre explicite la gestion de l'état. Par exemple, dans Haskell, vous pouvez considérer la monade d'état comme le cœur de l'état "normal" et la monographie IO) une représentation de l'état qui doit exister en dehors du programme. Ces deux monades permettent vous (a) représentez explicitement des actions avec état et (b) construisez des actions avec état en les composant à l'aide d'outils référentiellement transparents.
Vous référencez un certain nombre d'ORM qui, par leur nom, résument des bases de données sous forme d'ensembles d'objets. Vraiment, ce n'est pas ce que représentent les informations d'une base de données relationnelle! Par son nom, il représente des données relationnelles. SQL est une algèbre (langage) pour gérer les relations sur un ensemble de données relationnelles et est en fait assez "fonctionnel" lui-même. Je soulève ceci afin de considérer que (a) les ORM ne sont pas le seul moyen de mapper les informations de base de données, (b) que SQL est en fait un langage assez agréable pour certaines conceptions de base de données, et (c) que les langages fonctionnels ont souvent une algèbre relationnelle des mappages qui exposent la puissance de SQL de manière idiomatique (et dans le cas de Haskell, avec vérification typographique).
Je dirais que la plupart des lisps sont le langage fonctionnel d'un pauvre. Il est entièrement capable d'être utilisé selon les pratiques fonctionnelles modernes, mais comme il n'en a pas besoin, la communauté est moins susceptible de les utiliser. Cela conduit à un mélange de méthodes qui peuvent être très utiles mais obscurcit certainement la façon dont les interfaces fonctionnelles pures peuvent encore utiliser les bases de données de manière significative.
Je ne pense pas que la nature sans état des langages fp soit un problème de connexion aux bases de données. LISP est un langage de programmation fonctionnel non pur, il ne devrait donc pas avoir de problème avec l'état. Et les langages de programmation fonctionnels purs comme Haskell ont des façons de gérer les entrées et les sorties qui peuvent être appliquées à l'utilisation de bases de données.
D'après votre question, il semble que votre principal problème réside dans la recherche d'un bon moyen d'abstraire les données basées sur les enregistrements que vous récupérez de votre base de données en quelque chose qui est LISP-y (LISP-ish?) Sans avoir à écrire beaucoup de SQL code. Cela ressemble plus à un problème avec l'outillage/bibliothèques qu'à un problème avec le paradigme du langage. Si vous voulez faire du pur FP peut-être que LISP n'est pas le bon langage pour vous. Le LISP commun semble plus sur l'intégration des bonnes idées de oo, fp et d'autres paradigmes que sur le pur fp. Peut-être que vous devriez utilisez Erlang ou Haskell si vous voulez suivre la voie pure FP.
Je pense que les idées "pseudo-oo" dans LISP ont aussi leur mérite. Vous voudrez peut-être les essayer. S'ils ne correspondent pas à la façon dont vous souhaitez travailler avec vos données, vous pouvez essayer de créer une couche au-dessus de Weblocks qui vous permet de travailler avec vos données comme vous le souhaitez. Cela pourrait être plus facile que de tout écrire vous-même.
Avertissement: je ne suis pas un expert LISP. Je suis principalement intéressé par les langages de programmation et j'ai joué avec LISP/CLOS, Scheme, Erlang, Python et un peu de Ruby. Dans la vie de programmation quotidienne, je suis toujours obligé d'utiliser C #.
Si votre base de données ne détruit pas les informations, vous pouvez travailler avec elle de manière fonctionnelle en cohérence avec les valeurs de programmation "purement fonctionnelles" en travaillant dans les fonctions de la base de données entière en tant que valeur.
Si au moment T la base de données indique que "Bob aime Suzie", et que vous aviez une fonction qui accepte une base de données et un liker, alors tant que vous pouvez récupérer la base de données au moment T, vous avez un programme fonctionnel pur qui implique une base de données . par exemple.
# Start: Time T
likes(db, "Bob")
=> "Suzie"
# Change who bob likes
...
likes(db "Bob")
=> "Alice"
# Recover the database from T
db = getDb(T)
likes(db, "Bob")
=> "Suzie"
Pour ce faire, vous ne pouvez jamais jeter les informations que vous pourriez utiliser (ce qui signifie en pratique que vous ne pouvez pas jeter les informations), de sorte que vos besoins de stockage augmenteront de manière monotone. Mais vous pouvez commencer à travailler avec votre base de données comme une série linéaire de valeurs discrètes, où les valeurs suivantes sont liées aux précédentes par le biais de transactions.
C'est l'idée majeure derrière Datomic , par exemple.
Pas du tout. Il existe un genre de bases de données appelées "bases de données fonctionnelles", dont Mnesia est peut-être l'exemple le plus accessible. Le principe de base est que la programmation fonctionnelle est déclarative, elle peut donc être optimisée. Vous pouvez implémenter une jointure à l'aide de List Comprehensions sur les collections persistantes et l'optimiseur de requêtes peut déterminer automatiquement comment implémenter l'accès au disque.
Mnesia est écrit en Erlang et il y a au moins un framework web ( Erlyweb ) disponible pour cette plateforme. Erlang est intrinsèquement parallèle à un modèle de threading sans partage, donc, à certains égards, il se prête à des architectures évolutives.
Une base de données est le moyen idéal pour suivre l'état dans une API sans état. Si vous vous abonnez à REST, votre objectif est d'écrire du code sans état qui interagit avec une banque de données (ou un autre backend) qui assure le suivi des informations d'état de manière transparente afin que votre client n'ait pas à le faire.
L'idée d'un mappeur objet-relationnel, où vous importez un enregistrement de base de données en tant qu'objet puis le modifiez, est tout aussi applicable et utile à la programmation fonctionnelle qu'à la programmation orientée objet. La seule mise en garde est que la programmation fonctionnelle ne modifie pas le objet en place, mais l'API de base de données peut vous permettre de modifier le enregistrement en place. Le flux de contrôle de votre client ressemblerait à ceci:
La base de données mettra à jour l'enregistrement avec vos modifications. La programmation fonctionnelle pure peut interdire la réaffectation de variables dans le cadre de votre programme, mais votre API de base de données peut toujours autoriser les mises à jour sur place.
Je suis plus à l'aise avec Haskell. Le framework web Haskell le plus important (comparable à Rails et Django) s'appelle Yesod. Il semble avoir un ORM multi-backend assez cool, sûr pour les types. Jetez un œil à la - chapitre Persistance dans leur livre.
Les bases de données et la programmation fonctionnelle peuvent être fusionnées.
par exemple:
Clojure est un langage de programmation fonctionnel basé sur la théorie des bases de données relationnelles.
Clojure -> DBMS, Super Foxpro
STM -> Transaction,MVCC
Persistent Collections -> db, table, col
hash-map -> indexed data
Watch -> trigger, log
Spec -> constraint
Core API -> SQL, Built-in function
function -> Stored Procedure
Meta Data -> System Table
Remarque: Dans la dernière spécification 2, la spécification ressemble plus à RMDB. voir: spec-alpha2 wiki: Schema-and-select
Je préconise: Construire un modèle de données relationnelles au-dessus de la carte de hachage pour obtenir une combinaison d'avantages NoSQL et RMDB. Il s'agit en fait d'une implémentation inverse de posgtresql.
Duck Typing: Si cela ressemble à un canard et à des charlatans comme un canard, ce doit être un canard.
Si le modèle de données de clojure comme une RMDB, les installations de clojure comme une RMDB et la manipulation de données de clojure comme une RMDB, clojure doit être une RMDB.