Swift funcional

O que une “currying”, “mônadas”, “tipos de dados algébricos”? Não apenas o fato de alguns desenvolvedores estarem tentando contornar essas palavras, mas também a programação funcional. Sob a orientação cuidadosa de Evgeny Elchev, mergulhamos em um paradigma funcional e entendemos quase tudo. Não se assuste com antecedência, leia a transcrição da décima edição do podcast AppsCast .



Daniil Popov: Olá pessoal. Hoje, nosso convidado é Evgeny Elchev, da ensolarada Krasnoyarsk. Eugene, me diga o que você está fazendo e como você chegou à programação funcional?

Evgeny Elchev: Olá a todos. Sou desenvolvedor iOS da Redmadrobot, como todo mundo, pinto botões, às vezes escrevo lógica de negócios.

Eu me familiarizei com programação funcional através de artigos. Eu, sem entender bem o assunto, pensei que era uma espécie de programação procedural, sem classes. Quando li um dos artigos mais de perto, percebi que estava errado e comecei a cavar. Isso não quer dizer que eu acabei de chegar à programação funcional, pois seguidores reais se esforçam para escrever e escrevem em Haskell, usando mônadas sempre que possível. Eu apenas mergulhei e usei apenas na produção.

Daniil Popov: Então, as mônadas já se foram.

Evgeny Elchev: Já é difícil?

Daniil Popov: Tentei seguir o mesmo caminho, mas abri o artigo, vi as palavras “currying”, “mônada” e fechei imediatamente, pensando que ainda não era digno. Eu tenho uma chance?

Evgeny Elchev: Claro. Você pode não saber disso.

Em palavras simples sobre funcionalismo


Daniil Popov: Vamos dar uma definição simples para aqueles que nunca ouviram falar de um paradigma funcional.

Evgeny Elchev: Todo mundo entende paradigmas à sua maneira. Se pegarmos a explicação da Wikipedia, esse é o uso de funções matemáticas, em que todo o programa é interpretado como uma função matemática.

A abordagem funcional (FP) é quando você usa funções no seu trabalho que possuem apenas argumentos de entrada e um valor de saída. Se o programa inteiro consiste em tais funções, então este é um programa funcional.

Daniil Popov: OOP foi uma continuação lógica da programação processual usual e resolveu o problema de encapsular dados nas classes. Quais problemas a programação funcional deve resolver?

Evgeny Elchev: Os matemáticos inventaram a programação funcional. Os caras se reuniram e decidiram criar um paradigma onde tudo pode ser provado. Existe um código que ainda não foi lançado, mas provaremos tudo. Qualquer ponto do programa pode ser calculado, entendendo de onde iremos quando permitirmos alguma ação.

Parece abstrato, então vamos ver um exemplo de uma função pura. Escrevemos uma função de soma que leva dois argumentos, passa 2 e 3, obtém 5 e podemos provar. É sempre verdade. Se todo o nosso programa consistir em tais funções, tudo será comprovado.

Ao criar idiomas, as funções básicas começaram a ser perdidas e surgiram recursos adicionais: lambdas, funções de ordem superior, mônadas, monóides.

O paradigma funcional não resolve um único problema, é o mesmo desejo de escrever um bom código o mais simples possível, para que os programas sejam estáveis ​​e fáceis de manter.

Se você observar de perto, muitas das coisas que usamos no OOP são refletidas em uma abordagem funcional. Existem classes no OPP que encapsulam um conjunto de campos. No FP, isso também pode ser feito usando classes de tipo. Como Vitaly Bragilevsky gosta de dizer : "Se você olhar para o tablet em que os dados percorrem as linhas e as colunas de funções, o FI percorre as colunas, o OOP percorre as linhas". Isso é tudo.

Daniil Popov: Como a FI se relaciona com outros paradigmas? Posso escrever funcionalmente no OOP? Como misturar paradigmas, e isso faz sentido?

Evgeny Elchev: O paradigma é limitado ao fato de você escrever funções com dados. Uma das características do AF é a ausência de estados variáveis. Se seus dados são uma classe, não há problema. Se a classe for totalmente imutável, ela poderá ser usada. Uma classe é simplesmente um tipo, como uma string ou um número, apenas mais complexo, consistindo em vários valores.

Daniil Popov: Você disse anteriormente que pode provar a correção matemática de um programa se o escrever exclusivamente funcionalmente. Então a piada “compilada - funciona” para linguagens funcionais deixa de ser uma piada, certo?

Evgeny Elchev: Se você observar os erros de E / S, então sim. Anteriormente, os programadores lutavam com o problema: conectado à rede, não havia rede, nada retornava e tudo caía. Para a solução, a maneira mais fácil era verificar o que veio - nem / não, mas como havia o risco de que nem tudo fosse levado em consideração, o programa poderia compilar e travar.

Nas línguas modernas, isso é decidido. No Haskell, você pode escrever um programa que funcionará e não falhará, mas ninguém dirá como ele funciona corretamente. Obviamente, existem tipos estritos, e você não pode cometer um erro adicionando um número a uma sequência, mas sempre pode deixar bugs no aplicativo e ele funcionará.

Lugar da Abordagem Funcional no Swift


Alexei Kudryavtsev: Quanto o Swift pode ser chamado de linguagem funcional?

Evgeny Elchev: É possível. A funcionalidade é posicionada como sem estado, mas você pode escrever no Swift evitando esses estados. Ao mesmo tempo, Swift não é o mesmo que escrever no iOS, onde existem estados em todos os lugares. Obviamente, no Swift não há instruções especiais, como no Haskell, onde todas as funções são limpas por padrão e o compilador não permitirá que você acesse o estado e o altere. Se você marcar a função como "suja", as alterações ficarão disponíveis.

Alexei Kudryavtsev: No segundo ou terceiro Swift, havia um modificador puro, mas agia apenas no nível de compilação, para que os valores globais não mudassem. Você escreveu algo neles, mas o compilador cortou tudo.

Evgeny Elchev: Sim, no iOS, o compilador não seguirá isso. Tudo está inteiramente em nossa consciência: como você escreve, assim será.

Alexei Kudryavtsev: Você diz que existem muitos estados nos aplicativos para iOS, mas onde e o que fazer com eles se escrever em um estilo funcional?

Evgeny Elchev: O estado mais importante é a interface do usuário, por exemplo, campos de entrada. Praticamente nada pode ser feito com eles. Você pode tentar abstraí-los, coletar em um único local e escrever o máximo de código possível sem levá-los em consideração. Por exemplo, você escreve uma função suja que obtém todos os dados da interface do usuário.

No meu artigo , dei um exemplo de um formulário de autorização, onde é importante que o usuário insira um nome de usuário / senha. Escrevemos uma função suja que retorna uma estrutura com dados de autorização e, em seguida, escrevemos um código limpo nela. Obtivemos esses dados, validados, se o resultado for válido, envie uma solicitação ao servidor. Uma solicitação do servidor também é uma função suja e o processamento completo pode estar limpo. "Recebido, analisado" é uma função linear: a entrada para os dados, a saída é a nossa estrutura. Em seguida, eles foram transformados, filtrados e podem ser mostrados na tela novamente.

Alexei Kudryavtsev : Em Haskell, o compilador ajuda muito. Se o estado vier de algum lugar, toda a cadeia de chamadas será considerada suja e você precisará agrupar tudo em mônadas. Se a função for pura, o armazenamento em cache dos resultados funcionará - a mesma saída é sempre a mesma saída. No Swift, você mesmo precisa implementar os mapas e tentar retornar o resultado, se ele já estiver em cache.

Daniil Popov: A maioria das linguagens modernas é considerada multiparadigmática e muitas possuem recursos funcionais. Por exemplo, em Java, há uma anotação especial para a interface - @FunctionalInterface , que obriga o desenvolvedor a definir apenas um método na interface, para que essa interface na forma de lambdas seja usada em todo o código. Quando você adiciona um segundo método ou exclui um existente, o compilador começará a jurar que deixou de ser uma interface funcional. Além da plataforma iOS, o Swift possui esses recursos funcionais?

Evgeny Elchev: É difícil para mim entender o que essa anotação faz em Java. Se você quer dizer que implementa essa interface na classe e, em seguida, implementa apenas um método, não há restrições no Swift. Você pode criar tipos de letra, nomeá-lo e usá-lo como um tipo de função como um tipo de argumento, um tipo de variável para atribuir um fechamento. Você pode definir restrições - argumentos de fechamento de entrada e saída. As próprias funções de ordem superior que podem fechar são polimorfismos, e no Swift você pode criar polimorfismos em tipos, não limitados a objetos.

Mas não sei coisas funcionais específicas. Costumava haver curry no primeiro Swift, mas foi cortado. Agora podemos escrever uma função para nos curarmos, ou escrever uma função para que ela retorne fechamentos uma na outra, mas isso não está certo.

Não temos functores ou mônadas in a box. Eles nem podem ser escritos. Novos recursos no Swift 5.1 devem ajudar a fazer isso, mas tentei escrever esse código e o xCode caiu.

Em princípio, no Swift, se você desejar, é fácil fazer tudo sozinho. Já existe uma mônada opcional pronta para uso (em Haskell - talvez). Ela tem um mapa e um mapa plano para criar computação linear.

O Swift possui uma poderosa combinação de padrões. O switch, que existe em quase todos os idiomas e na maioria dos casos associa um número inteiro a uma unidade, pode mapear uma variável para um padrão, intervalos, tipos, extrair valores de tipos relacionados. Há cartago - você compõe um novo tipo, passando vários outros para ele. Com base neles, você também pode fazer a correspondência de padrões. Há uma enumeração que pode limitar tipos, vincular tipos relacionados a eles.

Alexei Kudryavtsev: Vou esclarecer que tipos relacionados são semelhantes às classes seladas de Kotlin. Essa é a enumeração dentro do caso em que você pode colocar o valor vinculado. No switch, você pode escrever: aqui está o caso, expanda, dentro do objeto. Por exemplo, casos de usuário e empresa com objetos correspondentes podem ser enum e alternados. Somente classes seladas são extensíveis e a troca é finita.

Por que um mobilista precisa de funcionalismo?


Daniil Popov: Como uma abordagem funcional é útil para o desenvolvimento móvel? Há algum problema que ele resolve?

Evgeny Elchev: Não há nenhum problema específico que possa ser resolvido precisamente com a ajuda da programação funcional.

O mais importante é que, seguindo esses princípios, mesmo que não dê certo, devemos abandonar as condições, porque elas são a principal dor.

Ao abandoná-los, você torna seu código mais compreensível. Não estou dizendo que haverá menos erros, porque isso deve ser pelo menos medido. No entanto, quando você começa a implementar algo, o código é alterado. Muitas vezes acontece que você analisa o código e tudo o que está nele, mas começa a reescrever, trocar, remover desnecessários e fáceis de ler.

Seguindo o paradigma funcional, você obtém uma fonte adicional de inspiração.

Daniil Popov: Se eu começar a escrever classes imutáveis ​​na linguagem OOP e usar métodos imutáveis, posso dizer que escrevo funcionalmente?

Evgeny Elchev: Sim, enquanto você começa a ver os profissionais. Está se tornando mais fácil testar métodos devido à falta de um estado global; é mais fácil compor uma cadeia de cálculos a partir de métodos.

Daniil Popov: No seu artigo, você explica o que são funções e efeitos colaterais puros. Você dá um exemplo com soma, onde a função também modifica o estado externo. O problema é que, quando você lê esse código, é difícil ter em mente todas as alterações: você precisa olhar para essa variável global, quem mais lê nela, quem mais escreve nela, o que pode acontecer. Mas a abordagem funcional permite que você permaneça no fluxo, não vá para as classes vizinhas, basta ler o código.

Alexei Kudryavtsev: Se você está em uma linguagem funcional, por um lado, é mais fácil escrever código, mas, por outro, precisa entender em que tipo de mônada você está agora.

Evgeny Elchev: Sim, mas quando você começa a escrever tudo sobre funções puras, outros problemas surgem. Por exemplo, como criar uma longa cadeia de cálculos. No estilo usual, sem pensar nisso, você despeja facilmente dados que não estavam lá inicialmente. Em uma abordagem funcional, isso não pode ser feito: você precisa quebrar as cadeias, conectar todos os cálculos usados ​​em vários métodos aos estados. Você precisa se acostumar com isso.

Por outro lado, diferentemente das classes no OPP, que tornam o código ossificado e difícil de compor, as funções podem ser mais flexíveis. Você pode escrever uma função, adicionar liberdade com a ajuda do fechamento, lançar essas funções e combiná-las em cadeias.

Alexei Kudryavtsev: Isso é semelhante à ideologia do Unix: existe bash, terminal e você pode transferir dados de pequenos programas que executam uma pequena ação para outros.

Daniil Popov: Isso me lembrou a abordagem Rx, onde eles escrevem correntes gigantes.

Evgeny Elchev: Vocês dois estão certos. E o Unix-way trata disso, e Rx é uma fusão da ideia de ligação e reatividade. No FP, vinculamos à fonte do evento e, na cadeia de cálculo, o alteramos, amarrando o resultado ao estado final.

Daniil Popov: As linguagens multiparadigma são boas, quão conveniente e útil é que a linguagem possa fazer isso e aquilo?

Evgeny Elchev: Se você seguir estritamente um paradigma, sempre haverá coisas que serão inconvenientes. Há coisas que são difíceis de alcançar em um estilo funcional, por exemplo, armazenamento de estado e criação de um cache.

Quando é possível escolher uma ferramenta mais adequada para uma tarefa específica - isso é legal.

Você pode criar uma classe, dentro dela, criar vários métodos em um estilo funcional e organizar o código de forma concisa em cadeias, ou abandonar a classe completamente, criar as funções necessárias e usá-las.

A desvantagem é que existe um dilema de escolha e quanto mais opções, mais difícil é escolher. Também está se tornando mais difícil de entender: quanto mais opções, mais difícil é ler o código.

Sobre Monad Jam


Alexei Kudryavtsev: Voltando ao funcionalismo, o que é uma mônada?

Evgeny Elchev: Eu chamaria isso de um contêiner no qual você pode combinar as cadeias de cálculos. A maneira mais simples é um contêiner no qual você pode aplicar a função e convertê-la em um novo contêiner com um valor modificado.

Imagine a caixa na qual o morango está, e existe um dispositivo que permite fazer geléia com morangos, mas você não pode colocar uma caixa de morangos nela, é necessário derramá-lo. Mônadas - é exatamente isso que permite que você coloque uma caixa no dispositivo.

Este não é um estado no sentido direto, pois o estado é armazenado separadamente, mas aqui está o contexto (caixa) com o valor e você passa de um para outro. Esta é a transferência de informações de um cálculo para outro.

Alexei Kudryavtsev: Acontece que, em uma abordagem funcional, para fazer geléia, você precisa entrar na caixa ...

Evgeny Elchev: A beleza é que você não precisa entrar na caixa. Você pode jogar uma caixa.

Funcionalidade para a elite?


Daniil Popov: Há uma opinião de que a programação funcional não pode ser praticada sem um doutorado em matemática. Isso é verdade?

Evgeny Elchev: Isso não é verdade. O conhecimento da matemática, é claro, melhora tudo, mas eu esqueci a matemática depois da formatura e vivo normalmente. De fato, todas essas são ferramentas incorporadas nas linguagens para resolver problemas específicos. Eles podem ser usados ​​sem tentar provar matematicamente. Enquanto você estiver compilando uma equação do ponto de vista matemático, será mais rápido e fácil lançar algumas linhas de código digitando, e elas funcionarão.

Alexei Kudryavtsev: Quanto um hobby para uma abordagem funcional interfere no desenvolvimento de produtos? Se parte do código já foi escrita funcionalmente, há alguma dificuldade em trabalhar com ele?

Evgeny Elchev: De maneira alguma. Se você não é um maníaco e não escreverá um ecossistema enorme com decoradores, poderá usar a mesma correspondência de padrões.

Será mais difícil se você quiser mudar para um novo elemento de funcionalismo. Por exemplo, o quinto Swift e a mônada de resultados apareceram recentemente, você não o havia usado antes, mas agora decidiu que tudo estará nele. Você leva a função de consulta para a rede e escreve que seu resultado agora é resultado (dados ou erro) e decide combinar com a próxima consulta, e aí você tem um fechamento separado com o valor e o erro e precisa reescrevê-lo. Comecei a escrever assim em um lugar, acordei dois dias depois, quando reescrevi metade do código, também criei novos invólucros para as bibliotecas se integrarem perfeitamente.

Por onde começar?


Daniil Popov: O que um iniciante deve ler para entender a programação funcional?

Evgeny Elchev: Precisamos usar uma linguagem puramente funcional, por exemplo, Haskell e experimentá-la na prática. Você pega um livro e faz os exemplos mais simples. Aqui você entende a abordagem - quando não existe, não é possível criar uma variável na qual é possível alterar o valor. Pessoalmente, certa vez peguei o livro “Aprenda Haskell em nome do bem”, onde tudo é descrito em linguagem simples. Depois disso, você pode começar a ler artigos na Internet: sobre a aparência das mônadas no Swift, sobre tipos de dados algébricos. Alguns artigos, e fica claro que isso não deve ter medo.

Daniil Popov : A coisa mais difícil é quebrar o paradigma em sua própria cabeça.

Evgeny Elchev: Não há necessidade de mergulhar profundamente na programação funcional. Muitas pessoas pensam que vão se sentar e começar a escrever funcionalmente - isso está errado.

Alexei Kudryavtsev: A coisa mais legal que vi foi um curso sobre Stepic de Haskell, de Denis Moskvin . Você começa adicionando alguns números e terminando com agrupando as mônadas em mônadas. E, se você quiser mudar completamente de idéia, ou seja, o livro "A estrutura da interpretação de programas de computador" é um curso no Lisp, de exemplos simples até o que você escreve um intérprete no Lisp.

Se o principal medo do funcionalismo já passou, dê uma olhada no relatório de Vitaliy Bragilevsky da AppsConf da primavera. No entanto, na temporada de outono do AppsConf, abordaremos tópicos não menos interessantes - a comunidade iOS está ansiosa por um relatório de Daniil Goncharov sobre engenharia reversa por Bluetooth , e os desenvolvedores de Android, juntamente com Alexander Smirnov, discutirão as abordagens atuais para criar animações

Source: https://habr.com/ru/post/pt462121/


All Articles