Swift funcional

¿Qué une "curry", "mónadas", "tipos de datos algebraicos"? No solo el hecho de que algunos desarrolladores están tratando de eludir estas palabras, sino también la programación funcional. Bajo la cuidadosa guía de Yevgeny Elchev, nos sumergimos en un paradigma funcional y entendimos casi todo. No se asuste con anticipación, no dude en leer la transcripción de la décima edición del podcast de AppsCast .



Daniil Popov: Hola a todos. Hoy, nuestro invitado es Evgeny Elchev del soleado Krasnoyarsk. Eugene, dime qué estás haciendo y cómo llegaste a la programación funcional.

Evgeny Elchev: Hola a todos. Soy desarrollador de iOS en Redmadrobot, como todos los demás, pinto botones, a veces escribo lógica de negocios.

Primero me familiaricé con la programación funcional a través de artículos. Yo, sin entender el punto, pensé que era una especie de programación de procedimiento, sin clases. Cuando leí más de cerca uno de los artículos, me di cuenta de que estaba equivocado y comencé a cavar. Esto no quiere decir que acabo de llegar a la programación funcional, ya que los seguidores reales pondrán sus huesos para ello y escribirán en Haskell, usando mónadas siempre que sea posible. Acabo de zambullirme y usarlo solo en producción.

Daniil Popov: Entonces, las mónadas ya se han ido.

Evgeny Elchev: ¿ Ya es difícil?

Daniil Popov: Traté de seguir el mismo camino, pero abrí el artículo, vi las palabras "curry", "mónada" e inmediatamente lo cerré, pensando que aún no era digno. ¿Tengo una oportunidad?

Evgeny Elchev: Por supuesto. Puede que no sepas esto en absoluto.

En palabras simples sobre funcionalismo


Daniil Popov: demos una definición simple para aquellos que nunca han oído hablar de un paradigma funcional.

Evgeny Elchev: Todos entienden los paradigmas a su manera. Si tomamos la explicación de Wikipedia, este es el uso de funciones matemáticas, donde todo el programa se interpreta como una función matemática.

El enfoque funcional (FP) es cuando utiliza funciones en su trabajo que solo tienen argumentos de entrada y un valor de salida. Si el programa completo consta de tales funciones, entonces este es un programa funcional.

Daniil Popov: OOP fue una continuación lógica de la programación procesal habitual y resolvió el problema de encapsular datos en clases. ¿Qué problemas debería resolver la programación funcional?

Evgeny Elchev: los matemáticos inventaron la programación funcional. Los muchachos se reunieron y decidieron crear un paradigma donde todo se puede probar. Hay código, aún no se ha lanzado, pero lo probaremos todo. Se puede calcular cualquier punto del programa, entendiendo de dónde iremos cuando permitamos alguna acción.

Suena abstracto, así que veamos un ejemplo de una función pura. Escribimos una función de suma que toma dos argumentos, le pasamos 2 y 3, obtenemos 5 y podemos probarlo. Siempre es cierto. Si todo nuestro programa consta de tales funciones, entonces todo es demostrable.

Al crear idiomas, comenzaron a perderse funciones básicas y aparecieron características adicionales: lambdas, funciones de orden superior, mónadas, monoides.

El paradigma funcional no resuelve un solo problema, es el mismo deseo de escribir un buen código lo más simple posible para que los programas sean estables y fáciles de mantener.

Si observa de cerca, muchas de las cosas que utilizamos en OOP se reflejan en un enfoque funcional. Hay clases en el OPP que encapsulan un conjunto de campos. En FP, esto también se puede hacer usando clases de tipo. Como a Vitaly Bragilevsky le gusta decir : "Si miras la tableta donde van los datos a lo largo de las líneas y las columnas de función, entonces el FI va a lo largo de las columnas, OOP va a lo largo de las líneas". Eso es todo

Daniil Popov: ¿Cómo se relaciona FI con otros paradigmas? ¿Puedo escribir funcionalmente en OOP? ¿Cómo mezclar paradigmas, y tiene sentido?

Evgeny Elchev: El paradigma se limita al hecho de que escribes funciones con datos. Una de las características de la FA es la ausencia de estados variables. Si sus datos son una clase, entonces no hay problema. Si la clase es completamente inmutable, entonces se puede usar. Una clase es simplemente un tipo, como una cadena o un número, solo que más complejo, que consta de varios valores.

Daniil Popov: Usted dijo anteriormente que puede probar la corrección matemática de un programa si lo escribe exclusivamente de manera funcional. Entonces la broma de "compilado - funciona" para los lenguajes funcionales deja de ser una broma, ¿verdad?

Evgeny Elchev: Si nos fijamos en los errores de E / S, entonces sí. Anteriormente, los programadores luchaban con el problema: conectado a la red, sin red, devuelto nulo y todo cayó. Para la solución, la forma más fácil era verificar lo que vino - nula / no nula, pero dado que existía el riesgo de que no todo se tuviera en cuenta, el programa podría compilarse y bloquearse.

En los idiomas modernos, esto se decide. En Haskell, puede escribir un programa que funcione y no se bloquee, pero nadie dirá cómo funciona correctamente. Por supuesto, hay tipos estrictos, y no puede cometer un error al agregar un número a una cadena, pero siempre puede dejar errores en la aplicación, y funcionará.

Lugar de aproximación funcional en Swift


Alexei Kudryavtsev: ¿Cuánto puede llamarse Swift un lenguaje funcional?

Evgeny Elchev: Es posible. La funcionalidad se posiciona como sin estado, pero puede escribir en Swift evitando tales estados. Al mismo tiempo, Swift no es lo mismo que escribir en iOS, donde hay estados en todas partes. Por supuesto, en Swift no hay instrucciones especiales como en Haskell, donde todas las funciones están limpias por defecto y el compilador no le permitirá acceder al estado y cambiarlo. Si marca la función como "sucia", los cambios estarán disponibles.

Alexei Kudryavtsev: En el segundo o tercer Swift, hubo un modificador puro, pero actuó solo en el nivel de compilación para que los valores globales no cambiaran. Escribiste algo en ellos, pero el compilador cortó todo.

Evgeny Elchev: Sí, en iOS el compilador no seguirá esto. Todo está enteramente en nuestra conciencia: mientras escribes, será así.

Alexei Kudryavtsev: Dices que hay muchos estados en las aplicaciones de iOS, pero ¿dónde y qué hacer con ellas si escribes con un estilo funcional?

Evgeny Elchev: El estado más importante es la interfaz de usuario, por ejemplo, los campos de entrada. Prácticamente no se puede hacer nada con ellos. Puede intentar abstraerse de ellos, recopilarlos en un solo lugar y escribir la mayor cantidad de código posible sin tenerlos en cuenta. Por ejemplo, escribe una función sucia que obtiene todos los datos de la interfaz de usuario.

En mi artículo di un ejemplo de un formulario de autorización, donde es importante que el usuario ingrese un nombre de usuario / contraseña. Escribimos una función sucia que devuelve una estructura con datos de autorización, y luego escribimos código limpio en ella. Obtuvimos estos datos, validados, si el resultado es válido, envíe una solicitud al servidor. Una solicitud de servidor también es una función sucia, y procesarla por completo puede estar limpia. "Recibido, analizado" es una función lineal: la entrada de datos, la salida es nuestra estructura. Luego se transformaron, filtraron y se pueden mostrar en la pantalla nuevamente.

Alexei Kudryavtsev : En Haskell, el compilador ayuda mucho. Si el estado proviene de algún lugar, toda la cadena de llamadas se considerará sucia y deberá envolver todo en mónadas. Si la función es pura, entonces el almacenamiento en caché de los resultados funciona: la misma salida es siempre la misma salida. En Swift, debe implementar los mapas usted mismo e intentar devolver el resultado si ya está en caché.

Daniil Popov: La mayoría de los lenguajes modernos se consideran multi-paradigmáticos y muchos tienen características funcionales. Por ejemplo, en Java hay una anotación especial para la interfaz: @FunctionalInterface , que obliga al desarrollador a definir solo un método en la interfaz, de modo que esta interfaz en forma de lambdas se use en todo el código. Cuando agrega un segundo método o elimina uno existente, el compilador comenzará a jurar que ha dejado de ser una interfaz funcional. ¿Swift, aparte de la plataforma iOS, tiene características tan funcionales?

Evgeny Elchev: Es difícil para mí entender qué hace una anotación en Java. Si quiere decir que implementa esta interfaz en la clase, y luego implementa solo un método, entonces no hay tales restricciones en Swift. Puede crear alias de tipo, nombrarlo y usarlo como un tipo de función como un tipo de argumento, un tipo de variable para asignar un cierre. Puede definir restricciones: argumentos de cierre de entrada y salida. Las funciones de orden superior que pueden tomar cierres son polimorfismos, y en Swift puede construir polimorfismos en tipos, no limitados a objetos.

Pero no sé cosas funcionales específicas. Solía ​​haber curry en el primer Swift, pero estaba cortado. Ahora podemos escribir una función para cursarnos, o escribir una función para que devuelva cierres entre sí, pero esto no es del todo correcto.

No tenemos functores ni mónadas en caja. Ni siquiera se pueden escribir. Las nuevas características en Swift 5.1 deberían ayudar a hacer esto, pero traté de escribir ese código y xCode se cayó.

En principio, en Swift, si lo desea, es fácil hacer todo usted mismo. Ya hay una mónada opcional fuera de la caja (en Haskell, tal vez). Ella tiene un mapa y un mapa plano para construir computación lineal.

Swift tiene una poderosa coincidencia de patrones. Switch, que existe en casi todos los idiomas y en la mayoría de los casos asocia un número entero con una unidad, puede asociar una variable con un patrón específico, rangos, tipos, extraer valores de tipos relacionados. Hay cartago: compones un nuevo tipo y le pasas varios más. En base a ellos, también puede hacer una coincidencia de patrones. Hay una enumeración que puede limitar los tipos, vincular los tipos relacionados con ellos.

Alexei Kudryavtsev: Aclararé que los tipos relacionados son similares a las clases selladas de Kotlin. Esta es la enumeración dentro del caso en el que puede poner el valor enlazado. A cambio, puede escribir: aquí está el caso, expandir, dentro del objeto. Por ejemplo, los casos de usuarios y empresas con los objetos correspondientes pueden ser enumerados y pueden cambiarse. Solo las clases selladas son extensibles y el interruptor es finito.

¿Por qué un movilista necesita funcionalismo?


Daniil Popov: ¿Cómo es útil un enfoque funcional para el desarrollo móvil? ¿Hay algún problema que resuelva?

Evgeny Elchev: No existe un problema específico que pueda resolverse precisamente con la ayuda de la programación funcional.

Lo más importante es que, siguiendo estos principios, incluso si no funciona, debemos abandonar las condiciones, porque son el principal dolor.

Al abandonarlos, haces que tu código sea más comprensible. No estoy diciendo que habrá menos errores, porque esto debe medirse al menos. Sin embargo, cuando comienza a implementar algo, el código cambia. A menudo sucede que mira el código y todo lo que contiene es el caso, pero comienza a reescribir, intercambiar, eliminar innecesarios y más fáciles de leer.

Siguiendo el paradigma funcional, obtienes una fuente adicional de inspiración.

Daniil Popov: Si empiezo a escribir clases tan inmutables en el lenguaje OOP y utilizando métodos inmutables, ¿puedo decir que escribo funcionalmente?

Evgeny Elchev: Sí, mientras comienzas a ver a los profesionales. Cada vez es más fácil probar métodos debido a la falta de un estado global, es más fácil componer una cadena de cálculos a partir de métodos.

Daniil Popov: En tu artículo, explicas qué es una función pura y los efectos secundarios. Usted da un ejemplo con sumatoria, donde la función también modifica el estado externo. El problema es que cuando lees dicho código, es difícil mantener todos los cambios en tu cabeza: debes mirar esta variable global, quién más lee en ella, quién más le escribe lo que puede suceder. Pero el enfoque funcional le permite permanecer en la secuencia, no ir a clases vecinas, solo leer el código.

Alexei Kudryavtsev: Si estás en un lenguaje funcional, por un lado es más fácil para ti escribir código, pero por otro lado, debes entender en qué tipo de mónada estás ahora.

Evgeny Elchev: Sí, pero cuando comienzas a escribir todo sobre funciones puras, surgen otros problemas. Por ejemplo, cómo construir una larga cadena de cálculos. En el estilo habitual, sin pensarlo, puede volcar fácilmente los datos que inicialmente no estaban allí. En un enfoque funcional, esto no se puede hacer: hay que romper las cadenas, conectar todos los cálculos utilizados en varios métodos a los estados. Tienes que acostumbrarte.

Por otro lado, a diferencia de las clases en OPP, que hacen que el código sea osificado y difícil de componer, las funciones pueden ser más flexibles. Puede escribir una función, agregar libertad con la ayuda del cierre, lanzar dichas funciones y combinarlas en cadenas.

Alexei Kudryavtsev: Esto es similar a la ideología de Unix: hay bash, terminal, y puedes transferir datos de pequeños programas que realizan una pequeña acción a otros.

Daniil Popov: Me recordó el enfoque de Rx, donde escriben cadenas gigantes.

Evgeny Elchev: Ambos tienen razón. Y Unix-way se trata de eso, y Rx es una fusión de la idea de unión y reactividad. En FP, nos unimos al origen del evento y en la cadena de cálculo lo cambiamos, vinculando el resultado al estado final.

Daniil Popov: ¿Son buenos los lenguajes de paradigmas múltiples? ¿Qué tan conveniente y útil es que el lenguaje pueda hacer esto y aquello?

Evgeny Elchev: Si sigues estrictamente algún tipo de paradigma, siempre habrá cosas que serán inconvenientes. Hay cosas que son difíciles de lograr en un estilo funcional, por ejemplo, almacenar el estado y hacer un caché.

Cuando es posible elegir una herramienta que sea más adecuada para una tarea específica, esto es genial.

Puede crear una clase, dentro de ella realizar varios métodos en un estilo funcional y organizar el código de forma concisa en cadenas, o abandonar la clase por completo, realizar las funciones necesarias y usarlas.

La desventaja es que hay un dilema de elección y cuantas más opciones, más difícil es elegir. También se está volviendo más difícil de entender: cuantas más opciones, más difícil es leer el código.

Sobre Monad Jam


Alexei Kudryavtsev: Volviendo al funcionalismo, ¿qué es una mónada?

Evgeny Elchev: Yo lo llamaría un contenedor en el que puedes combinar las cadenas de cálculos. La forma más simple es un contenedor al que puede aplicar la función y convertirla en un nuevo contenedor con un valor modificado.

Imagina la caja en la que se encuentra la fresa, y hay un dispositivo que te permite hacer mermelada con fresas, pero no puedes poner una caja de fresas en ella, debes verterla. Mónadas: esto es lo que le permite colocar una caja en el dispositivo.

Este no es un estado en el sentido directo, ya que el estado se almacena por separado, pero aquí está el contexto (cuadro) con el valor y se pasa de uno a otro. Esta es la transferencia de información de un cálculo a otro.

Alexei Kudryavtsev: Resulta que en un enfoque funcional, para hacer mermelada, debes meterte dentro de la caja ...

Evgeny Elchev: La belleza es que no tienes que meterte en la caja. Puedes tirar una caja.

¿Funcionalidad para la élite?


Daniil Popov: Existe la opinión de que la programación funcional no se puede practicar sin un doctorado en matemáticas. ¿Es esto cierto?

Evgeny Elchev: Esto no es cierto. El conocimiento de las matemáticas, por supuesto, mejora todo, pero olvidé las matemáticas después de graduarme y vivir normalmente. De hecho, todas estas son herramientas que están incorporadas en los idiomas para resolver problemas específicos. Se pueden usar sin intentar demostrar matemáticamente. Si bien compilará una ecuación desde un punto de vista matemático, será más rápido y fácil tirar un par de líneas de código escribiendo, y funcionarán.

Alexei Kudryavtsev: ¿Cuánto puede un pasatiempo para un enfoque funcional interferir con el desarrollo del producto? Si parte del código ya está escrito funcionalmente, ¿hay alguna dificultad para trabajar con él?

Evgeny Elchev: Para nada. Si no eres un maníaco y no vas a escribir un gran ecosistema con decoradores, entonces puedes usar la misma coincidencia de patrones.

Será más difícil si quieres cambiar a un nuevo elemento de funcionalismo. Por ejemplo, el quinto Swift y la mónada resultante aparecieron recientemente, no lo había usado antes, pero ahora decidió que todo estará en él. Lleva la función de consulta a la red y escribe que su resultado ahora es resultado (ya sea datos o error), y decide combinar con la siguiente consulta, y allí tiene un cierre separado con el valor y el error, y necesita reescribirlo. Comencé a escribir así en un lugar, me desperté dos días después, cuando reescribí la mitad del código, también hice nuevos envoltorios para que las bibliotecas se integraran maravillosamente.

Por donde empezar


Daniil Popov: ¿Qué debería leer un principiante para comprender la programación funcional?

Evgeny Elchev: Necesitamos tomar un lenguaje puramente funcional, por ejemplo, Haskell e intentarlo en la práctica. Tomas un libro de texto y haces los ejemplos más simples. Aquí comprende el enfoque: cuando no hay para, no puede crear una variable en la que pueda cambiar el valor. Personalmente, una vez tomé el libro "Learn Haskell in the name of good", donde todo se describe en un lenguaje simple. Después de eso, puede comenzar a leer artículos en Internet: sobre cómo se ven las mónadas en Swift, sobre los tipos de datos algebraicos. Un par de artículos, y queda claro que esto no debe temer.

Daniil Popov : Lo más difícil es romper el paradigma en tu propia cabeza.

Evgeny Elchev: No es necesario sumergirse bruscamente en la programación funcional. Muchas personas piensan que se sentarán y comenzarán a escribir funcionalmente, esto está mal.

Alexei Kudryavtsev: Lo mejor que vi fue un curso sobre Stepic de Haskell de Denis Moskvin . Empiezas sumando un par de números y termina envolviendo las mónadas en mónadas. Y si desea romper completamente su mente, es decir, el libro "La estructura de la interpretación de los programas de computadora" es un curso en Lisp, desde ejemplos simples hasta lo que escribe un intérprete de Lisp en Lisp.

Si el miedo principal al funcionalismo ha pasado, eche un vistazo al informe de Vitaliy Bragilevsky de la AppsConf de primavera. Sin embargo, en la temporada de otoño de AppsConf tocaremos temas no menos interesantes: la comunidad de iOS espera un informe de Daniil Goncharov sobre ingeniería inversa de Bluetooth , y los desarrolladores de Android junto con Alexander Smirnov discutirán los enfoques actuales para crear animaciones

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


All Articles