Bancos de dados e programação funcional estão em desacordo?

122

Sou desenvolvedor web há algum tempo e recentemente comecei a aprender programação funcional. Como outros, tive alguns problemas significativos ao aplicar muitos desses conceitos ao meu trabalho profissional. Para mim, a principal razão para isso é que vejo um conflito entre o objetivo da FP de permanecer apátrida parece bastante em desacordo com o fato de que a maioria dos trabalhos de desenvolvimento da Web que eu fiz foi fortemente vinculada a bancos de dados, que são muito centrados em dados.

Uma coisa que me tornou um desenvolvedor muito mais produtivo foi a descoberta de mapeadores de objetos relacionais, como MyGeneration d00dads para .Net, Class :: DBI para perl, ActiveRecord para ruby, etc. Isso me permitiu ficar longe desde escrever, inserir e selecionar instruções o dia todo, e concentrar-se em trabalhar com os dados facilmente como objetos. Obviamente, eu ainda poderia escrever consultas SQL quando a energia delas fosse necessária, mas, caso contrário, ela seria bem resumida nos bastidores.

Agora, voltando-se para a programação funcional, parece que em muitas estruturas da Web FP, como o Links, é necessário escrever muito código sql padrão, como neste exemplo . Weblocks parece um pouco melhor, mas parece usar um tipo de modelo OOP para trabalhar com dados e ainda exige que o código seja escrito manualmente para cada tabela em seu banco de dados, como neste exemplo . Suponho que você use alguma geração de código para escrever essas funções de mapeamento, mas isso parece decididamente não-cibernético.

(Observe que não observei os Weblocks ou Links com muita atenção; talvez eu esteja entendendo mal como eles são usados).

Portanto, a pergunta é: para as partes de acesso ao banco de dados (que eu acredito que são muito grandes) do aplicativo Web ou outro desenvolvimento que requer interface com um banco de dados sql, parecemos ser forçados a seguir um dos seguintes caminhos:

  1. Não use programação funcional
  2. Acessar dados de uma maneira irritante e sem abstração, que envolve escrever manualmente muitos códigos SQL ou semelhantes a SQL
  3. Force a nossa linguagem funcional a um paradigma de pseudo-POO, removendo assim um pouco da elegância e estabilidade da verdadeira programação funcional.

Claramente, nenhuma dessas opções parece ideal. Encontrou uma maneira de contornar esses problemas? Existe mesmo um problema aqui?

Nota: Pessoalmente, eu estou mais familiarizado com o LISP na parte frontal do FP, portanto, se você quiser dar exemplos e conhecer vários idiomas FP, o lisp provavelmente será o idioma preferido

PS: Para questões específicas para outros aspectos do desenvolvimento da Web, consulte esta pergunta .

Tristan Havelick
fonte
4
Confira o ClojureQL e o HaskellDB. São camadas de abstração utilizando álgebra relacional.
Masse
10
Você está começando com a premissa errada. A programação funcional é sobre o gerenciamento explícito e seguro do estado. Eles funcionam muito bem com bancos de dados, na verdade.
Lucian
3
O SQL é uma das linguagens focadas em programação funcional mais bem-sucedidas, não acho que exista alguma dificuldade inerente.
Douglas
3
Seus nºs 2 e 3 são uma falsa dicotomia. Escrever SQL bruto não é algo que deve ser necessariamente evitado, e abstrações em um banco de dados não precisam necessariamente ser do tipo OOP.
Dan Burton

Respostas:

45

Antes de tudo, eu não diria que o CLOS (Common Lisp Object System) é "pseudo-OO". É OO de primeira classe.

Segundo, acredito que você deve usar o paradigma que se adapta às suas necessidades.

Você não pode armazenar dados sem estado, enquanto uma função é fluxo de dados e realmente não precisa de estado.

Se você tem várias necessidades misturadas, misture seus paradigmas. Não se limite a usar apenas o canto inferior direito da sua caixa de ferramentas.

Svante
fonte
3
só por diversão, pensei em mencionar o registro de dados, que tenta ser um banco de dados sem estado. ele registra todas as ações, como "o usuário gosta da publicação 1233". Essas ações são resolvidas para o verdadeiro estado do banco de dados. A chave é que as consultas são fatos apenas em vez de mutação ...
Chet
80

Chegando a isso da perspectiva de uma pessoa de banco de dados, acho que os desenvolvedores de front-end se esforçam demais para encontrar maneiras de fazer com que os bancos de dados se ajustem ao seu modelo, em vez de considerar as maneiras mais eficazes de usar banco de dados que não são orientados a objetos ou funcionais, mas relacionais e usando teoria de conjuntos. Vi isso geralmente resultar em código com baixo desempenho. Além disso, cria um código difícil de ajustar o desempenho.

Ao considerar o acesso ao banco de dados, há três considerações principais: integridade dos dados (por que todas as regras de negócios devem ser aplicadas no nível do banco de dados e não através da interface do usuário), desempenho e segurança. O SQL foi escrito para gerenciar as duas primeiras considerações com mais eficiência do que qualquer linguagem de front-end. Porque é projetado especificamente para fazer isso. A tarefa de um banco de dados é muito diferente da tarefa de uma interface do usuário. É de se admirar que o tipo de código mais eficaz no gerenciamento da tarefa seja conceitualmente diferente?

E os bancos de dados mantêm informações críticas para a sobrevivência de uma empresa. É de se admirar que as empresas não estejam dispostas a experimentar novos métodos quando sua sobrevivência está em risco. Heck muitas empresas não estão dispostas a atualizar para novas versões do banco de dados existente. Portanto, existe um conservadorismo inerente no design do banco de dados. E é deliberadamente assim.

Eu não tentaria escrever T-SQL ou usar conceitos de design de banco de dados para criar sua interface de usuário. Por que você tentaria usar sua linguagem de interface e conceitos de design para acessar meu banco de dados? Porque você acha que o SQL não é chique (ou novo) o suficiente? Ou você não se sente confortável com isso? Só porque algo não se encaixa no modelo com o qual você se sente mais confortável, não significa que seja ruim ou errado. Isso significa que é diferente e provavelmente diferente por uma razão legítima. Você usa uma ferramenta diferente para uma tarefa diferente.

HLGEM
fonte
"O SQL foi escrito para gerenciar as duas primeiras considerações com mais eficiência do que qualquer linguagem de front-end." Sério? Por que, então, é que as restrições de SQL ainda não pode fazer coisas como esta ?
Robin Green
Mas um gatilho pode, esse é um dos principais objetivos dos gatilhos para lidar com restrições complexas.
HLGEM
2
Eu moveria seu último parágrafo para que ele seja o primeiro parágrafo. Um argumento muito bom, que ecoa o que os outros também estão insistindo, que é uma abordagem com vários paradigmas (não de tamanho único).
pestófago
31

Você deve ler o artigo "Out of the Tar Pit" de Ben Moseley e Peter Marks, disponível aqui: "Out of the Tar Pit" (6 de fevereiro de 2006)

É um clássico moderno que detalha um paradigma / sistema de programação chamado Programação Funcional-Relacional. Embora não esteja diretamente relacionado aos bancos de dados, ele discute como isolar interações com o mundo externo (bancos de dados, por exemplo) do núcleo funcional de um sistema.

O artigo também discute como implementar um sistema em que o estado interno do aplicativo é definido e modificado usando uma álgebra relacional, que obviamente está relacionada a bancos de dados relacionais.

Este documento não fornecerá uma resposta exata de como integrar bancos de dados e programação funcional, mas ajudará você a projetar um sistema para minimizar o problema.

Kevin Albrecht
fonte
4
Que papel ótimo. O link que você dá é quebrado (temporariamente?), Mas eu achei o papel também em shaffner.us/cs/papers/tarpit.pdf
pestophagous
2
@queque O link original ainda está morto. Coloquei o novo link na resposta de Kevin.
David Tonhofer
25
  1. As linguagens funcionais não têm o objetivo de permanecer sem estado, elas têm o objetivo de tornar explícito o gerenciamento de estado. Por exemplo, em Haskell, você pode considerar a mônada do Estado como o coração do estado "normal" e a mônada IO como uma representação do estado que deve existir fora do programa. Ambas as mônadas permitem (a) representar explicitamente ações com estado e (b) criar ações com estado, compondo-as usando ferramentas referencialmente transparentes.

  2. Você faz referência a vários ORMs que, por seu nome, abstraem bancos de dados como conjuntos de objetos. Na verdade, não é isso que as informações em um banco de dados relacional representam! Por seu nome, ele representa dados relacionais. SQL é uma álgebra (linguagem) para lidar com relacionamentos em um conjunto de dados relacionais e, na verdade, é bastante "funcional". Trago isso à tona para considerar que (a) ORMs não são a única maneira de mapear informações de banco de dados, (b) que SQL é realmente uma linguagem bastante agradável para alguns projetos de banco de dados e (c) que linguagens funcionais geralmente têm álgebra relacional mapeamentos que expõem o poder do SQL de uma maneira idiomática (e no caso de Haskell, verificado de forma tipográfica).

Eu diria que a maioria dos lisps é a linguagem funcional de um homem pobre. É totalmente capaz de ser usado de acordo com práticas funcionais modernas, mas como não exige, é menos provável que a comunidade as use. Isso leva a uma mistura de métodos que podem ser altamente úteis, mas certamente obscurecem como interfaces funcionais puras ainda podem usar bancos de dados de maneira significativa.

J. Abrahamson
fonte
15

Eu não acho que a natureza sem estado das linguagens fp seja um problema com a conexão com bancos de dados. Lisp é uma linguagem de programação funcional não pura, portanto não deve ter nenhum problema em lidar com o estado. E linguagens de programação funcionais puras, como Haskell, têm maneiras de lidar com entradas e saídas que podem ser aplicadas ao uso de bancos de dados.

Da sua pergunta, parece que seu principal problema está em encontrar uma boa maneira de abstrair os dados baseados em registros que você recebe do banco de dados em algo que é lisp-y (lisp-ish?) Sem precisar escrever muito SQL código. Isso parece mais um problema com as ferramentas / bibliotecas do que um problema com o paradigma da linguagem. Se você quer fazer FP puro, talvez o cisco não seja o idioma certo para você. Lisp comum parece mais sobre integrar boas idéias de oo, fp e outros paradigmas do que sobre fp puro. Talvez você deva usar Erlang ou Haskell se quiser seguir a rota FP pura.

Eu acho que as idéias de 'pseudo-oo' no lisp também têm seu mérito. Você pode experimentá-los. Se eles não se encaixam na maneira como você deseja trabalhar com seus dados, tente criar uma camada sobre Weblocks que permita trabalhar com os dados da maneira que desejar. Isso pode ser mais fácil do que escrever tudo sozinho.

Disclaimer: Eu não sou um especialista em Lisp. Estou interessado principalmente em linguagens de programação e tenho tocado com Lisp / CLOS, Scheme, Erlang, Python e um pouco de Ruby. Na vida diária da programação, ainda sou forçado a usar C #.

Mendelt
fonte
3
Erlang não é FP puro por nenhuma definição da palavra. Você escreve erlang criando muitos processos (todos em execução em paralelo) que enviam mensagens uns para os outros, como objetos em, digamos, smalltalk. Portanto, de uma perspectiva de alto nível, pode até parecer um pouco absurdo e definitivamente tem estado. Se você aumentar o zoom (no código executado dentro de um processo), ele parecerá mais funcional, mas ainda não é puramente funcional, pois você poderá enviar mensagens (e, portanto, E / S etc) de qualquer lugar que desejar, além de armazenar " variáveis ​​globais "(global para o processo, dentro de algo chamado" dict do processo ").
Amadiro
@Amadiro "definitivamente tem estado". Claro que sim. Nós sempre temos estado. O problema não é estado, é estado mutável . Uma boa parte da "programação funcional" consiste em livrar-se das representações de estado que são quebradiças (por exemplo, instâncias de objetos que são alteradas por outros processos enquanto mantemos uma referência a elas, de maneira não transacional, para adicionar insulto à lesão). Erlang possui um estado não mutável e, portanto, atinge este critério de programação funcional. E assim: bancos de dados de qualquer tipo nunca são um problema. As atualizações do banco de dados são um problema (consulte também a afirmação desagradável no Prolog).
precisa saber é o seguinte
15

Se seu banco de dados não destruir informações, você poderá trabalhar com elas de maneira funcional, consistente com os valores de programação "puramente funcionais", trabalhando nas funções de todo o banco de dados como um valor.

Se no momento T o banco de dados declara que "Bob gosta de Suzie" e você possui uma função que aceita um banco de dados e um liker, desde que você possa recuperar o banco de dados no momento T, você tem um programa funcional puro que envolve um banco de dados . por exemplo

# 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"

Para fazer isso, você nunca pode descartar as informações que você pode usar (o que, com toda a praticidade, significa que você não pode descartar as informações), portanto, suas necessidades de armazenamento aumentarão monotonicamente. Mas você pode começar a trabalhar com seu banco de dados como uma série linear de valores discretos, em que os valores subsequentes estão relacionados aos anteriores por meio de transações.

Essa é a principal idéia por trás da Datomic , por exemplo.

animal
fonte
Agradável. Eu nem sabia sobre Datomic. Veja também: justificativa para Datomic .
David Tonhofer
12

De modo nenhum. Há um gênero de bancos de dados conhecido como 'Bancos de Dados Funcionais', dos quais Mnesia é talvez o exemplo mais acessível. O princípio básico é que a programação funcional é declarativa, portanto pode ser otimizada. Você pode implementar uma junção usando Compreensões de lista em coleções persistentes, e o otimizador de consulta pode descobrir automaticamente como implementar o acesso ao disco.

Mnesia está escrito em Erlang e há pelo menos um framework da Web ( Erlyweb ) disponível para essa plataforma. Erlang é inerentemente paralelo a um modelo de encadeamento de nada compartilhado; portanto, de certa maneira, ele se presta a arquiteturas escalonáveis.

ConcernedOfTunbridgeWells
fonte
1
Eu não acho que isso seja uma solução. Também existem bancos de dados orientados a objetos, mas geralmente você deseja se conectar a um banco de dados SQL relacional simples e antigo.
jalf
4
Você ainda tem uma incompatibilidade de impedância com idiomas e bancos de dados OO da mesma maneira que uma incompatibilidade de impedância de SQL funcional.
ConcernedOfTunbridgeWells
1
@ConcernedOfTunbridgeWells Declararei arbitrariamente que essa incompatibilidade de impedância é uma invenção da imaginação de pessoas com martelos que precisam de tudo para serem unhas. Camadas muito finas e conhecimento sobre SQL podem percorrer um longo caminho, portanto jOOQ e shims semelhantes.
David Tonhofer
6

Um banco de dados é a maneira perfeita de acompanhar o estado em uma API sem estado. Se você se inscrever no REST, seu objetivo é escrever um código sem estado que interaja com um armazenamento de dados (ou algum outro back-end) que rastreie as informações de estado de maneira transparente, para que seu cliente não precise.

A idéia de um Mapeador Objeto-Relacional, em que você importa um registro de banco de dados como um objeto e depois o modifica, é tão aplicável e útil à programação funcional quanto à programação orientada a objetos. A única ressalva é que a programação funcional não modifica o objeto no local, mas a API do banco de dados pode permitir que você modifique o registro no local. O fluxo de controle do seu cliente ficaria assim:

  • Importe o registro como um objeto (a API do banco de dados pode bloquear o registro neste momento),
  • Leia o objeto e o ramo com base em seu conteúdo, como desejar,
  • Empacote um novo objeto com as modificações desejadas,
  • Passe o novo objeto para a chamada de API apropriada que atualiza o registro no banco de dados.

O banco de dados atualizará o registro com suas alterações. A programação funcional pura pode impedir a reatribuição de variáveis no escopo do seu programa , mas a API do banco de dados ainda pode permitir atualizações no local.

Brice frito
fonte
5

Estou mais confortável com Haskell. A estrutura da Web Haskell mais proeminente (comparável ao Rails e Django) é chamada Yesod. Parece ter um ORM muito legal, com segurança de tipo e de back-end. Dê uma olhada no capítulo Persistência no livro.

Honza Pokorny
fonte
0

Bancos de dados e programação funcional podem ser fundidos.

por exemplo:

Clojure é uma linguagem de programação funcional baseada na teoria de bancos de dados relacionais.

               Clojure -> DBMS, Super Foxpro
                   STM -> TransactionMVCC
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

Nota: Na última especificação2, a especificação é mais parecida com o RMDB. veja: wiki spec-alpha2: Esquema e seleção

Defendo: Construir um modelo de dados relacionais sobre o mapa de hash para obter uma combinação de vantagens NoSQL e RMDB. Esta é realmente uma implementação reversa do posgtresql.

Digitação de pato: Se parece um pato e grasna como um pato, deve ser um pato.

Se o modelo de dados do clojure como um RMDB, as instalações do clojure como um RMDB e a manipulação de dados do clojure como um RMDB, o clojure deve ser um RMDB.

Clojure é uma linguagem de programação funcional baseada na teoria de bancos de dados relacionais

Tudo é RMDB

Implementar modelo de dados relacionais e programação com base no mapa de hash (NoSQL)

Lin Pengcheng
fonte