<div dir="ltr"><div>Respondo a Chema sobre los "closures", capturas y los "side effects"</div><div><br></div><div>Uffff, me obligas a ser un poco más simpático y poner alguna url ;-)</div><div><br></div><div><br></div><div>El concepto de closure fue "inventado" por Alonzo Church en lambda-cálculus</div><div><br></div><div><div></div></div><div> The concept of closures was developed in the 1960s for the mechanical evaluation of expressions in the <a href="https://en.wikipedia.org/wiki/%CE%9B-calculus" class="gmail-mw-redirect" title="Λ-calculus">λ-calculus</a> <br></div><div> <a href="https://en.wikipedia.org/wiki/Closure_(computer_programming)">https://en.wikipedia.org/wiki/Closure_(computer_programming)</a></div><div><br></div><div><br></div><div>El cálculo lambda es muy funcional, y muuuuchos conceptos de la programación fucnional vienen de aquí. De hecho, el lambda-calculus es la respuesta "funcional" de Church a la idea de la máquina de Turing (imperativa donde lo haya)</div><div><br></div><div>En el cálculo lambda no sólo habla de closures, sino que todo son "potencialmente" closures ;-)</div><div>Con "potencialmente" quiero decir que no necesitas especificarlo, si lo es o no lo es, depende de si tú capturas o no capturas (y para capturar, sólo tienes que utilizar, no declarar intenciones)</div><div><br></div><div>Y en Haskell, como tantas otras cosas en Haskell, es igual que lamda-calculus en este punto<br></div><div><br></div><div>Estoy que lo tiro (otra url, en una búsqueda rápida, eso sí)<br></div><div></div><div><br></div><div><br></div><div>Esto es sobre haskell...</div><div><br></div><div><a href="https://wiki.haskell.org/Closure">https://wiki.haskell.org/Closure</a></div><div><h1 id="gmail-firstHeading" class="gmail-firstHeading" lang="en">Closure</h1>
<div id="gmail-bodyContent" class="gmail-mw-body-content">
</div><p>A closure, the opposite of a <a href="https://wiki.haskell.org/Combinator" title="Combinator">combinator</a>, is a function that makes use of <a href="https://wiki.haskell.org/Free_variable" title="Free variable">free variables</a> in its definition. It 'closes' around some portion of its environment. for example
</p>
<div class="gmail-mw-highlight gmail-mw-content-ltr" dir="ltr"><pre><span class="gmail-nf">f</span> <span class="gmail-n">x</span> <span class="gmail-ow">=</span> <span class="gmail-p">(</span><span class="gmail-nf">\</span><span class="gmail-n">y</span> <span class="gmail-ow">-></span> <span class="gmail-n">x</span> <span class="gmail-o">+</span> <span class="gmail-n">y</span><span class="gmail-p">)</span>
</pre></div></div><div><br></div><div>Para el que no conozca mucho Haskell, el closure es lo que está entre paréntesis, que es una función lambda, que en este caso, ha capturado la x y por eso se ha "promocionado a un closure" ;-)</div><div>Como veis, igual que en lambda-calculus, si es un "closure" o es un "combinator" depende de si "capturas" o no<br></div><div><br></div><div>Por cierto el símbolo \ pretende parecerse a una lambda (es lo más parecido en teclado normal) y eso es lo que es, es una lambda de lambda cálculus, que "captura" la x que no es un parámeto de la propia lambda (el parámetro es y, no x)</div><div><br></div><div>El logotipo de Haskell, no tiene por casualidad una lambda ;-)<br></div><div><br></div><div><img src="cid:ii_kjuctums0" alt="image.png" width="267" height="189"></div><div><br></div><div><br></div><div>Tanto Haskell como lambda-calculus son poco sospechosos de tener "muchos" "side effects" y ambos tienen "funciones anónimas" que "capturan"</div><div><br></div><div>Decías chema...</div><div><br></div><div><span style="color:rgb(0,0,255)"><i>Si una misma expresión lambda, ante el mismo argumento da resultados distintos, entonces el orden en que hagas su evaluación
cambia el resultado. Estaría incumpliendo el principio de transparencia referencial</i></span></div><div><br></div><div>Creo que aquí estás pensando en python u otro lenguaje bastante imperativo donde ciertamente un "closure" muy fácilmente puede provocar "side effects" (aunque también es muy normal que las "funciones" de python tengan "efectos"), pero en haskell o lambda-cálculus los closures no pueden añadir "side effects"</div><div>Para añadir "efectos colaterales" tienes que recurrir a un "truco/trampa" muy específico y nada tiene que ver con los closures</div><div><br></div><div>Me extendería mucho más, pero... creo que para una lista de Python, hablar tan poco de Python... ya me he excedido ;-)</div><div><br></div><div><br></div><div><br></div><div>Saludos<br></div><div><br></div><br><div><br></div><div><br></div><div><br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jan 12, 2021 at 3:57 PM Francisco José Fernández Naranjo <<a href="mailto:fjfnaranjo@gmail.com">fjfnaranjo@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Sobre lo que comentas al principio Chema, y sobre lo que dice Guido,<br>
siempre me resultó curioso una cosa a la que pocas veces se hace<br>
referencia cuando se habla de este tema.<br>
<br>
Tendemos a hablar de ciertas capacidades del lenguaje como si fueran<br>
aspiracionales. Por ejemplo, a pensar que usar programación funcional<br>
(o las opciones pseudo-funcionales) es "mejor" (o más pythónico) para<br>
ciertos casos. Y tendemos por tanto a decidir que cuando alguien no<br>
usa una característica del lenguaje el problema es de comprensión de<br>
la característica en sí por su parte (cuando no eres directamente<br>
condescendiente y piensas que es un inútil). Pero yo lo que me<br>
encuentro en mi día a día cuando trabajo es un mundo donde la gente<br>
tiene unas habilidades muy diversas y donde no quieres que todo el<br>
mundo sea una persona extremadamente capaz en muchas cosas, si no<br>
donde quieres un grupo de personas que se complementen.<br>
<br>
Concretando, bajar al nivel de la programación funcional, sobre todo<br>
si no se comprende bien de manera no-funcional, va a agregar<br>
complejidad al código o el diseño que va a repercutir en la capacidad<br>
de otras personas para leerlo, comentarlo, mantenerlo... Esto aplica<br>
también a conocimientos específicos, como por ejemplo, operar correcta<br>
y eficientemente con datos usando cython, o entender como llevar un<br>
proceso muy complejo a una librería en C para usarla desde Python a<br>
más alto nivel.<br>
<br>
Cuando yo me encuentro con esto, siempre me ha gustado mucho el<br>
programador que sabe escribir esa complejidad en la parte más profunda<br>
del diseño, y deja las operaciones más universales en la parte<br>
superior del mismo. Eso permite simultáneamente combinar la capacidad<br>
específica de ese programador con los compañeros que no han<br>
desarrollado tanto en ese área.<br>
<br>
Una de las cosas que comentas al final, cuando dices "hay mucho<br>
potencial en python que no se usa bien o se ignora que está ahí" (y<br>
donde solapas un poco con la opinión de Guido) deja un poco fuera la<br>
decisión de ese programador pragmático que saber medir muy bien donde<br>
poner su aprendizaje y esfuerzo para cumplir simultáneamente con el<br>
"Simple is better than complex" y con el "Complex is better than<br>
complicated". Creo que dos figuras en la comunidad de Python que<br>
representan un poco esta diferencia de apreciación por lo pragmático<br>
son Guido y Raymond Hettinger. Donde ves por una lado el programador<br>
que aspira a la belleza en lo "complex" y el que aspira a la belleza<br>
en lo "simple".<br>
<br>
Y en ese caso, creo que es muy acertado la decisión que hasta ahora se<br>
ha tomado en el diseño de Python de que algunas características tienen<br>
mejor lugar en paquetes como "functools". Formando parte como dices de<br>
ese compendio de buenas ideas de otros lenguajes. Y esa "pena" que ves<br>
al final, se puede ver también como una de las grandes fortalezas del<br>
lenguaje.<br>
<br>
Un saludo,<br>
Naranjo.<br>
<br>
<br>
<br>
<br>
On Tue, Jan 12, 2021 at 2:56 PM Chema Cortés <<a href="mailto:pych3m4@gmail.com" target="_blank">pych3m4@gmail.com</a>> wrote:<br>
><br>
> Hola, José Luis:<br>
><br>
> Guido suele decir que las características funcionales de python se usan muy poco porque no se llegan a entender. Ese fue el motivo, al menos, para que la función reduce dejara de estar en builtins y acabara en el módulo functools.<br>
><br>
> El cálculo lambda de Church sí que está relacionado con la transparencia referencial, pero no dice nada de clausuras. La idea es que mediante "aplicaciones" y "reducciones" transformes una expresión lambda hasta llegar a un valor, independientemente del orden en que hagas las transformaciones.<br>
><br>
> Si una misma expresión lambda, ante el mismo argumento da resultados distintos, entonces el orden en que hagas su evaluación cambia el resultado. Estaría incumpliendo el principio de transparencia referencial.<br>
><br>
> Si se encapsula una función con una clausura para mantener estados, los resultados de la función podrían ser distintos ante los mismos argumentos. No podemos sustituir la llamada a la función por su resultado, por lo que se incumple la transparencia referencial. En realidad, solemos decir más que la función tiene "efectos colaterales".<br>
><br>
> En haskell, no existen los "contextos" como los llamas. Todo son "transformaciones" entre unos valores de entrada y de salida. Para mantener "estados" se usan las "transformaciones monádicas", que no son "clausuras". No se almacena ningún estado, sólo indicas de qué estado a qué estado pasas, sin ninguna persistencia entre llamadas.<br>
><br>
> En cuanto a las características de programación funcional que pueda tener python, muy pocos las usan o son conscientes de que las usan. Python, junto con su librería estándar, es un compendio de conocimientos aplicados que han sido traídos desde otros lenguajes. Mucho de lo que llamamos "modo pythónico" de hacer las cosas no son otra cosa que transformaciones funcionales con una teoría detrás que ignoramos mayormente: compresiones de listas, tipos algebraicos, dataclases, ordenación por clave, etc, etc.En mi opinión, hay mucho potencial en python que no se usa bien o se ignora que está ahí.<br>
><br>
> Seguro que Guido hace bien en mantener un lenguaje python accesible para todos y que no quiera liarla más con conceptos de programación funcional. Pero me parece una pena.<br>
><br>
><br>
> Saludos.<br>
><br>
><br>
><br>
> El lun, 11 ene 2021 a las 21:18, Jose Luis (<<a href="mailto:jleahred@gmail.com" target="_blank">jleahred@gmail.com</a>>) escribió:<br>
>><br>
>> Una pequeña observación.<br>
>><br>
>> Güido ha explicado de forma brillante y coherente en alguna ocasión, que Python no es ni pretende ser "muy" funcional<br>
>><br>
>> Los "closures" de Python (hay que matizar, porque cada uno define closure como le da la gana), capturan "cosas" (llámalo estado si quieres), OK<br>
>> Pero eso no va en contra de la transparencia referencial.<br>
>><br>
>> Las lambdas de Haskell capturan "cosas" y es muy raro que rompan la transparencia referencial.<br>
>><br>
>> De hecho, el concepto closure diría que viene del cálculo lambda (del genial "profe" de Turing, Alonzo Church), y simplificando, son funciones anónimas (aka lambdas) que capturan y tampoco rompen la transparencia referencial (es la gracia del cálculo lambda vs la máquina de Turing ;-)<br>
>><br>
>> Una función lambda con un contexto difernte, es una función diferente. Si tú le pones el mismo nombre "mutando" o haces mutaciones del contexto... puedes romper la transparencia referencial, pero no es por el concepto de "closure" o captura<br>
>> Vamos, que si capturar "cosas" (aka contexto, aka estado, aka variables, aka "llámalo como quieras") violara la transparencia referencial, Haskell no sería muy funcional (ni el cálculo lambda) ;-)<br>
>><br>
>> Reitero que no tengo claro que forzar "mucho" la programación funcional en Python sea buena idea.<br>
>> Que yo lo opine, poco valor tiene, pero considero a Güido como un tipo coherente y muy inteligente, del que he aprendido un montón de cosas con sus reflexiones y explicaciones; y corregidme si me equivoco, pero diría que Güido no es partidario de "forzar" python con programación funcional (si fuera un tipo más serio y simpático, mandaría los enlaces de Güido al respecto, pero los tengo que buscar, estoy cansado y tampoco dispongo de tiempo en este momento)<br>
>><br>
>><br>
>> Saludos de un humilde amante de la programación funcional (amor no correspondido; ni me quiere la programación funcional, ni la imperativa, pero sigo dando guerra ;-)<br>
>><br>
>> On Wed, Jan 6, 2021 at 5:31 PM Chema Cortes <<a href="mailto:pych3m4@gmail.com" target="_blank">pych3m4@gmail.com</a>> wrote:<br>
>>><br>
>>><br>
>>> El mar, 5 ene 2021 a las 16:19, Jesus Cea (<<a href="mailto:jcea@jcea.es" target="_blank">jcea@jcea.es</a>>) escribió:<br>
>>>><br>
>>>> On 4/1/21 11:18, Chema Cortes wrote:<br>
>>>> > La recursividad en python es muy limitada. Pero lo que viene a descubrir<br>
>>>> > es algo que se podría haber hecho mejor con generadores. En cuanto al<br>
>>>> > otro uso, sería una aplicación parcial de argumentos (functools.partial).<br>
>>>> ><br>
>>>> > Aún así, hay algoritmos recursivos que no son tan fácilmente<br>
>>>> > transcribibles en iterables.<br>
>>>><br>
>>>> Más allá del uso chorras para convertir una función concreta de<br>
>>>> recursiva a iterativa, para mí lo interesante del artículo es el uso de<br>
>>>> "closures" para generar funciones personalizadas que se llevan su estado<br>
>>>> consigo. No todo necesita el coste de crear clases e instancias<br>
>>>> explícitas, no todo se puede solucionar con generadores y<br>
>>>> "functools.partial()" no proporciona estado mutable.<br>
>>>><br>
>>>> El uso de "closures" explícitos me parece algo que vale la pena añadir a<br>
>>>> la caja de herramientas, siempre vigilando que no existe una forma más<br>
>>>> elegante de hacer algo concreto.<br>
>>>><br>
>>><br>
>>> Aunque suene bien que las funciones mantengan su propio estado, nunca es aconsejable que las funciones tengan efectos colaterales (violación de la transparencia referencial).<br>
>>><br>
>>> Las clausuras son importantes y están en el fondo de muchos recursos como pueden ser los decoradores. Pero es siempre posible crear un generador (Iterable) en lugar de una función con clausura (Callable). La diferencia es que los generadores están protocolizados, con opciones para poner fin a la iteración (StopIteration), para inyectar datos a la clausura (método .send()) o para destruir el generador (GeneratorExit). También se pueden usar en gran parte de la librería estándar, con las funciones de itertools como las más potentes.<br>
>>><br>
>>> Por comparar, la función de fibonacci como generador:<br>
>>><br>
>>> from itertools import islice<br>
>>><br>
>>> def fib():<br>
>>> a, b = 1, 1<br>
>>> while True:<br>
>>> yield a<br>
>>> a, b = b, a+b<br>
>>><br>
>>> def fib_generator(n):<br>
>>> return next(islice(fib(), n-1, None))<br>
>>><br>
>>><br>
>>> Si se compara, es casi el doble de rápida que la que viene en el artículo, fib_closure.<br>
>>><br>
>>> Saludos.<br>
>>><br>
>>><br>
>>> --<br>
>>> Hyperreals *R "Quarks, bits y otras criaturas infinitesimales": <a href="https://blog.ch3m4.org" rel="noreferrer" target="_blank">https://blog.ch3m4.org</a><br>
>>> Buscador Python Hispano: <a href="http://busca.ch3m4.org" rel="noreferrer" target="_blank">http://busca.ch3m4.org</a><br>
>>> _______________________________________________<br>
>>> Asociación Python España: <a href="http://www.es.python.org/" rel="noreferrer" target="_blank">http://www.es.python.org/</a><br>
>>> Python Madrid: <a href="http://www.python-madrid.es/" rel="noreferrer" target="_blank">http://www.python-madrid.es/</a><br>
>>> Madrid mailing list<br>
>>> <a href="mailto:Madrid@lists.es.python.org" target="_blank">Madrid@lists.es.python.org</a><br>
>>> <a href="https://lists.es.python.org/listinfo/madrid" rel="noreferrer" target="_blank">https://lists.es.python.org/listinfo/madrid</a><br>
><br>
> _______________________________________________<br>
> Asociación Python España: <a href="http://www.es.python.org/" rel="noreferrer" target="_blank">http://www.es.python.org/</a><br>
> Python Madrid: <a href="http://www.python-madrid.es/" rel="noreferrer" target="_blank">http://www.python-madrid.es/</a><br>
> Madrid mailing list<br>
> <a href="mailto:Madrid@lists.es.python.org" target="_blank">Madrid@lists.es.python.org</a><br>
> <a href="https://lists.es.python.org/listinfo/madrid" rel="noreferrer" target="_blank">https://lists.es.python.org/listinfo/madrid</a><br>
</blockquote></div>