Pergunta

Eu recentemente vindo a aprender sobre programação funcional (especificamente Haskell, mas eu já passei por tutoriais sobre Lisp e Erlang também). Enquanto eu encontrei os conceitos muito esclarecedor, eu ainda não vejo o lado prático do conceito "sem efeitos colaterais". Quais são as vantagens práticas do mesmo? Eu estou tentando pensar na mentalidade funcional, mas existem algumas situações que apenas parecem demasiado complexas sem a capacidade de salvar o estado de uma forma fácil (Eu não considero monads de Haskell 'fácil').

Vale a pena continuar a aprender Haskell (ou outra linguagem puramente funcional) em profundidade? É programação funcional ou apátrida realmente mais produtivo do que processual? É provável que eu vou continuar a usar Haskell ou outra linguagem funcional mais tarde, ou devo aprender isso só para o entendimento?

Eu me importar menos sobre o desempenho do que a produtividade. Então eu estou pedindo, principalmente, se eu vou ser mais produtivo em uma linguagem funcional do que um / processual orientada a objeto / whatever.

Foi útil?

Solução

Leia programação funcional em um Nutshell .

Há muitas vantagens para a programação apátrida, não menos do que é dramaticamente multithreaded e de código concorrente. Para ser franco, estado mutável é inimigo do código multithreaded. Se os valores são imutáveis ??por padrão, os programadores não precisa se preocupar com um fio de mutação do valor do estado compartilhado entre dois threads, por isso elimina toda uma classe de multithreading bugs relacionados a condições de corrida. Como não há condições de corrida, não há nenhuma razão para usar fechaduras ou, então, a imutabilidade elimina toda uma outra classe de bugs relacionados a impasses também.

Essa é a grande razão pela qual as questões de programação funcional, e, provavelmente, o melhor para saltar sobre o trem de programação funcional. Há também grande quantidade de outros benefícios, incluindo simplificado de depuração (ou seja, as funções são pura e não fazer estado mutação em outras partes de um aplicativo), mais concisa e código expressivo, menos código clichê comparação com linguagens que são fortemente dependentes de padrões de projeto, e o compilador pode mais agressivamente otimizar seu código.

Outras dicas

Os mais peças de seu programa são apátridas, os mais maneiras existem para colocar pedaços juntos sem ter quebrar nada . O poder das mentiras apátrida paradigma não em apatridia (ou pureza) per se , mas a capacidade que lhe dá para escrever poderoso, reutilizável funções e combiná-los.

Você pode encontrar um bom tutorial com muitos exemplos no papel de John Hughes Por Matters funcionais programação (PDF).

Você será gobs mais produtivo, especialmente se você escolher uma linguagem funcional, que também tem tipos de dados algébricos e casamento de padrões (CAML SML, Haskell).

Muitas das outras respostas têm-se centrado no lado performance (paralelismo) da programação funcional, que eu acredito que é muito importante. No entanto, você pedir especificamente sobre a produtividade, como em, você pode programar a mesma coisa mais rápida em um paradigma funcional do que em um paradigma imperativo.

Na verdade, eu encontrar (por experiência pessoal) que programar em F # combina a maneira que eu acho melhor, e por isso é mais fácil. Eu acho que é a maior diferença. Eu tenho programado em ambos F # e C #, e há muito menos "combater a língua" em F #, que eu amo. Você não tem que pensar sobre os detalhes em F #. Eis alguns exemplos do que eu descobri que eu realmente gosto.

Por exemplo, mesmo que F # é digitado estaticamente (todos os tipos são resolvidas em tempo de compilação), os números de inferência do tipo que tipos você tem, então você não tem que dizer isso. E se ele não pode descobrir isso, ele automaticamente faz a sua função / classe / whatever genérico. Então, você nunca tem que escrever qualquer genérico que seja, é tudo automático. Acho que isso significa que eu estou gastando mais tempo pensando sobre o problema e menos como implementá-lo. Na verdade, sempre que voltar para C #, eu acho que eu realmente sinto falta esse tipo de inferência, você nunca perceber o quão distrair é até que você não precisa mais fazer isso.

Também em F #, em vez de escrever loops, você chamar funções. É uma mudança sutil, mas significativa, porque você não tem que pensar sobre a construção de loop mais. Por exemplo, aqui está um pedaço de código que iria passar e combinar alguma coisa (não me lembro o que, é de um projeto Euler quebra-cabeça):

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

Eu percebo que fazendo um filtro, em seguida, um mapa (que é uma conversão de cada elemento) em C # seria bastante simples, mas você tem que pensar em um nível inferior. Particularmente, você teria que escrever o circuito em si, e ter o seu próprio, se declaração explícita, e esses tipos de coisas. Desde aprender F #, eu percebi que eu achei mais fácil de código na forma funcional, onde se pretende filtrar, você escreve "filtro", e se você deseja mapear, você escreve "mapa", em vez de implementar cada um dos detalhes.

Eu também adoro o |> operador, que eu acho que separa F # de ocaml e, possivelmente, outras linguagens funcionais. É o operador pipe, ele permite que você "pipe" a saída de uma expressão para a entrada de outra expressão. Isso torna o código a seguir como eu penso mais. Como no trecho de código acima, que está dizendo, "tomar a seqüência de fatores, filtro, então mapeá-lo." É um nível muito alto de pensamento, que você não entrar em uma linguagem de programação imperativa, porque você está tão ocupado escrevendo o loop e se declarações. É a coisa que eu mais sinto falta quando eu ir para outro idioma.

Então, só em geral, embora eu possa programar em C # e F #, acho que é mais fácil de usar F #, porque você pode pensar em um nível superior. Eu diria que, porque os pequenos detalhes são removidos da programação funcional (em F #, pelo menos), que eu sou mais produtivo.

Editar : vi em um dos comentários que você pediu um exemplo de "estado" em uma linguagem de programação funcional. F # pode ser escrito imperativamente, então aqui está um exemplo direto de como você pode ter estado mutável em F #:

let mutable x = 5
for i in 1..10 do
    x <- x + i

Considere todos os bugs difíceis que você passou um longo tempo de depuração.

Agora, quantos desses erros foram devido a "interações inesperadas" entre dois componentes separados de um programa? (Quase todos os erros de threading têm esta forma: corridas envolvendo gravação de dados compartilhados, os impasses, ... Além disso, é comum encontrar bibliotecas que têm algum efeito inesperado sobre o estado global, ou ler / escrever o registro / meio ambiente, etc.) < em> I iria postular que a queda de pelo menos 1 em cada 3 'erros duras' enquadram nesta categoria.

Agora, se você mudar para apátrida / imutável / programação pura, todos os bugs ir embora. Você é apresentado com alguns novos desafios em vez (por exemplo, quando você do quiser módulos diferentes para interagir com o ambiente), mas em uma linguagem como Haskell, essas interações se explicitamente reificada no sistema tipo, o que significa que você pode apenas olhar para o tipo de função e da razão sobre o tipo de interações que pode ter com o resto do programa.

Essa é a grande vitória da 'imutabilidade' IMO. Em um mundo ideal, nós todos APIs projeto óptimo e até mesmo quando as coisas eram mutáveis, efeitos seriam local e bem documentado e interações dos inesperados 'seriam mantidos a um mínimo. No mundo real, há muitas APIs que interagem com o estado global de inúmeras formas, e estes são a fonte dos erros mais perniciosos. Aspirando a apatridia aspira a se livrar da indesejada / implícito / interações por trás das cenas entre os componentes.

Uma das vantagens de funções apátridas é que eles permitem precalculation ou cache de valores de retorno da função. Mesmo alguns compiladores C permitem marcar explicitamente funciona como apátridas para melhorar a sua optimisability. Como muitos outros já observou, as funções de apátridas são muito mais fáceis a complementar.

Mas a eficiência não é a única preocupação. A função pura é mais fácil de testar e depurar uma vez que qualquer coisa que afete é explicitamente declarado. E ao programar em uma linguagem funcional, tem-se o hábito de fazer o menor número de funções "suja" (com I / O, etc.) quanto possível. Separar o material stateful desta forma é uma boa maneira de conceber programas, mesmo em linguagens não-tão-funcionais.

As linguagens funcionais pode levar um tempo para "pegar", e é difícil explicar a alguém que não tenha passado por esse processo. Mas a maioria das pessoas que persistem o tempo suficiente finalmente perceber que o barulho é pena, mesmo se eles não acabam usando linguagens funcionais muito.

Sem estado, é muito fácil de paralelizar automaticamente seu código (como CPUs são feitas com mais e mais núcleos Isto é muito importante).

Eu escrevi um post sobre este assunto apenas algum tempo atrás: sobre a importância da Pureza .

aplicações web apátridas são essenciais quando você começa a ter maior tráfego.

Pode haver uma abundância de dados do usuário que você não deseja armazenar no lado do cliente por razões de segurança, por exemplo. Neste caso, você precisa armazená-lo do lado do servidor. Você pode usar a sessão de aplicações web padrão, mas se você tiver mais de uma instância do aplicativo que você vai precisar para se certificar de que cada usuário está sempre direcionado para a mesma instância.

Os balanceadores de carga, muitas vezes têm a capacidade de ter 'sessões persistentes', onde o balanceador de carga de alguma forma sabe qual servidor para enviar os usuários solicitar a. Esta não é, porém, ideal, por exemplo, isso significa que cada vez que você reiniciar o aplicativo web, todos os usuários conectados perderá sua sessão.

A melhor abordagem é para armazenar a sessão por trás dos servidores web em algum tipo de armazenamento de dados, estes dias, há cargas de grandes produtos NoSQL disponíveis para este (Redis, mongo, ElasticSearch, memcached). Desta forma, os servidores web são apátridas, mas você ainda tem estado do lado do servidor e da disponibilidade deste estado pode ser gerenciado por escolher a configuração do armazenamento de dados direita. Esses armazenamentos de dados geralmente têm grande redundância por isso deve ser quase sempre possível fazer alterações em sua aplicação web e até mesmo o armazenamento de dados sem impactar os usuários.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top