Retorno considerado perjudicial? ¿Puede el código ser funcional sin él?


45

OK, entonces el título es un poco clickbaity, pero en serio he estado diciendo, no pidas patada por un tiempo. Me gusta cómo fomenta que los métodos se utilicen como mensajes de manera orientada a objetos. Pero esto tiene un problema persistente que ha estado dando vueltas en mi cabeza.

He llegado a sospechar que un código bien escrito puede seguir los principios OO y los principios funcionales al mismo tiempo. Estoy intentando conciliar estas ideas y el gran escollo que he aterrizado en es return.

Una función pura tiene dos cualidades:

  1. Llamarlo repetidamente con las mismas entradas siempre da el mismo resultado. Esto implica que es inmutable. Su estado se establece solo una vez.

  2. No produce efectos secundarios. El único cambio causado al llamarlo es producir el resultado.

Entonces, ¿cómo hace uno para ser puramente funcional si usted ha renunciado a utilizar returncomo su forma de comunicar los resultados?

La idea tell, no ask funciona usando lo que algunos considerarían un efecto secundario. Cuando trato con un objeto no le pregunto sobre su estado interno. Le digo lo que debo hacer y utiliza su estado interno para averiguar qué hacer con lo que le dije que hiciera. Una vez que lo cuento, no pregunto qué hizo. Solo espero que haya hecho algo sobre lo que le dijeron que hiciera.

Pienso en Tell, Don't Ask como algo más que un nombre diferente para la encapsulación. Cuando uso returnno tengo idea de lo que me llamó. No puedo hablar de su protocolo, tengo que obligarlo a tratar con mi protocolo. Que en muchos casos se expresa como el estado interno. Incluso si lo que está expuesto no es exactamente el estado, generalmente es solo un cálculo realizado en el estado y los argumentos de entrada. Tener una interfaz para responder ofrece la oportunidad de combinar los resultados en algo más significativo que el estado interno o los cálculos. Ese es el mensaje que pasa . Ver este ejemplo .

En el pasado, cuando las unidades de disco en realidad tenían discos y una unidad de memoria USB era lo que hacía en el automóvil cuando la rueda estaba demasiado fría para tocarla con los dedos, me enseñaron cómo las personas molestas consideran las funciones que no tienen parámetros. void swap(int *first, int *second)Parecía muy útil, pero nos animaron a escribir funciones que devolvieran los resultados. Entonces tomé esto en serio por fe y comencé a seguirlo.

Pero ahora veo personas que construyen arquitecturas donde los objetos permiten que la forma en que fueron construidas controlen dónde envían sus resultados. Aquí hay un ejemplo de implementación . Inyectar el objeto del puerto de salida parece un poco como la idea del parámetro out nuevamente. Pero así es como los objetos tell-don't-ask le dicen a otros objetos lo que han hecho.

Cuando supe por primera vez acerca de los efectos secundarios, pensé que era el parámetro de salida. Nos dijeron que no sorprendiéramos a las personas al hacer que parte del trabajo se llevara a cabo de una manera sorprendente, es decir, al no seguir la return resultconvención. Ahora claro, sé que hay una pila de problemas de subprocesos asincrónicos paralelos con los que los efectos secundarios se resuelven, pero el retorno es realmente solo una convención que hace que dejes el resultado en la pila para que lo que llames puedas sacarlo más tarde. Eso es todo lo que realmente es.

Lo que realmente estoy tratando de preguntar:

¿Es returnla única manera de evitar toda la miseria de los efectos secundarios y obtener seguridad para el hilo sin bloqueos, etc. O puedo seguir diciéndole, no pregunte de una manera puramente funcional?


3
Si elige no ignorar la separación de consultas de comandos, ¿consideraría resuelto su problema?
rwong

30
Tenga en cuenta que encontrarse en una patada puede ser una indicación de que está participando en un diseño basado en dogmas en lugar de razonar los pros y los contras de cada situación específica.
Blrfl

3
En general, cuando las personas hablan de que el "retorno" es perjudicial, dicen que está en contra de la programación estructurada, no funcional, y una sola declaración de retorno al final de la rutina (y tal vez al final de ambos lados de un bloque if / else que es en sí mismo el último elemento) no está incluido en eso.
Random832

16
@jameslarge: falsa dicotomía. No permitirte dejarse llevar por los diseños por el pensamiento dogmático no es lo mismo que el Salvaje Oeste / todo se aproxima de lo que estás hablando. El punto es no permitir que el dogma se interponga en el camino de un código bueno, simple y obvio. La ley universal es para los desamparados; El contexto es para los reyes.
Nicol Bolas

44
Hay una cosa para mí que no está clara en tu pregunta: dices que has renunciado a usar 'return', pero por lo que puedo decir, no expliques explícitamente eso con tus otros conceptos o digas por qué lo has hecho. Cuando se combina con la definición de una función pura que incluye producir un resultado, se crea algo que es imposible de resolver.
Danikov

Respuestas:


96

Si una función no tiene ningún efecto secundario y no devuelve nada, entonces la función es inútil. Es tan simple como eso.

Pero supongo que puedes usar algunos trucos si quieres seguir la letra de las reglas e ignorar el razonamiento subyacente. Por ejemplo, usar un parámetro out es, estrictamente hablando, no usar un retorno. Pero todavía hace exactamente lo mismo que un retorno, solo que de una manera más complicada. Entonces, si cree que el retorno es malo por alguna razón , entonces usar un parámetro out es claramente malo por las mismas razones subyacentes.

Puedes usar más trucos complicados. Por ejemplo, Haskell es famoso por el truco de la mónada IO en el que puede tener efectos secundarios en la práctica, pero aún así, estrictamente hablando, no tiene efectos secundarios desde un punto de vista teórico. El estilo de paso continuo es otro truco, que le permite evitar devoluciones al precio de convertir su código en espagueti.

La conclusión es que, en ausencia de trucos tontos, los dos principios de las funciones libres de efectos secundarios y "sin retorno" simplemente no son compatibles. Además, señalaré que ambos son principios realmente malos (dogmas realmente) en primer lugar, pero esa es una discusión diferente.

Reglas como "decir, no preguntar" o "sin efectos secundarios" no se pueden aplicar universalmente. Siempre tienes que considerar el contexto. Un programa sin efectos secundarios es literalmente inútil. Incluso los lenguajes funcionales puros lo reconocen. Más bien se esfuerzan por separar las partes puras del código de las que tienen efectos secundarios. El objetivo de las mónadas estatales o de E / S en Haskell no es que evites los efectos secundarios, porque no puedes, sino que la presencia de los efectos secundarios está explícitamente indicada por la firma de la función.

La regla tell-dont-ask se aplica a un tipo diferente de arquitectura: el estilo en el que los objetos del programa son "actores" independientes que se comunican entre sí. Cada actor es básicamente autónomo y encapsulado. Puede enviarle un mensaje y decide cómo reaccionar, pero no puede examinar el estado interno del actor desde el exterior. Esto significa que no puede saber si un mensaje cambia el estado interno del actor / objeto. El estado y los efectos secundarios están ocultos por diseño .


20
@CandiedOrange: si un método tiene efectos secundarios directa o indirectamente, aunque muchas capas de llamadas no cambian nada conceptualmente. Sigue siendo un efecto secundario. Pero si el punto es que los efectos secundarios solo ocurren a través de objetos inyectados para que pueda controlar qué tipos de efectos secundarios son posibles, entonces esto suena como un buen diseño. Simplemente no es libre de efectos secundarios. Es bueno OO pero no es puramente funcional.
JacquesB

12
@LightnessRacesinOrbit: grep en modo silencioso tiene un código de retorno de proceso que se utilizará. Si no tuviera eso, entonces sería realmente inútil
unperson325680

10
@progo: un código de retorno no es un efecto secundario; Es el efecto. Todo lo que llamamos efecto secundario se llama efecto secundario porque no es el código de retorno;)
Lightness compite con Monica

8
@Cubic ese es el punto. Un programa sin efectos secundarios es inútil.
Jens Schauder

55
@mathreadler Eso es un efecto secundario :)
Andres F.

32

Tell, Don't Ask viene con algunos supuestos fundamentales:

  1. Estás usando objetos.
  2. Tus objetos tienen estado.
  3. El estado de sus objetos afecta su comportamiento.

Ninguna de estas cosas se aplica a las funciones puras.

Así que repasemos por qué tenemos la regla "Dile, no preguntes". Esta regla es una advertencia y un recordatorio. Se puede resumir así:

Permita que su clase administre su propio estado. No le pregunte por su estado, y luego tome medidas basadas en ese estado. Dile a la clase lo que quieres y deja que decida qué hacer en función de su propio estado.

Para decirlo de otra manera, las clases son las únicas responsables de mantener su propio estado y actuar en consecuencia. De esto se trata la encapsulación.

De Fowler :

Tell-Don't-Ask es un principio que ayuda a las personas a recordar que la orientación a objetos se trata de agrupar datos con las funciones que operan en esos datos. Nos recuerda que, en lugar de pedir datos a un objeto y actuar sobre esos datos, deberíamos decirle a un objeto qué hacer. Esto nos anima a mover el comportamiento a un objeto para ir con los datos.

Para reiterar, nada de esto tiene nada que ver con funciones puras, o incluso impuras, a menos que exponga el estado de una clase al mundo exterior. Ejemplos:

Infracción de TDA

var color = trafficLight.Color;
var elapsed = trafficLight.Elapsed;
If (color == Color.Red && elapsed > 2.Minutes)
    trafficLight.ChangeColor(green);

No es una violación de TDA

var result = trafficLight.ChangeColor(Color.Green);

o

var result = await trafficLight.ChangeColorWhenReady(Color.Green);     

En los dos últimos ejemplos, el semáforo retiene el control de su estado y sus acciones.


Espera un segundo, los cierres pueden ser puros. tienen estado (lo llaman alcance léxico). y ese alcance léxico puede afectar su comportamiento. ¿Estás seguro de que TDA solo es relevante cuando hay objetos involucrados?
candied_orange

77
Los cierres de @CandiedOrange solo son puros si no modifica los enlaces cerrados. De lo contrario, pierde la transparencia referencial al llamar a la función devuelta desde el cierre.
Jared Smith

1
@JaredSmith, ¿y no es lo mismo cuando hablas de objetos? ¿No es este simplemente el problema de la inmutabilidad?
candied_orange

1
@bdsl - Y ahora, sin darse cuenta, ha señalado exactamente a dónde va cada discusión de este tipo con su ejemplo de trafficLight.refreshDisplay. Si sigue las reglas, conduce a un sistema altamente flexible que nadie más que el codificador original entiende. Incluso apostaría a que después de un par de años, incluso el codificador original probablemente tampoco entenderá lo que hicieron.
Dunk

1
"Tonto", como en "No abras los otros objetos y no mires sus tripas, sino que derrama tus propias tripas (si tienes alguna) en los métodos de otros objetos; ese es el Camino".
Joker_vD

30

Cuando trato con un objeto no le pregunto sobre su estado interno. Le digo lo que debo hacer y utiliza su estado interno para averiguar qué hacer con lo que le dije que hiciera.

No solo pregunta por su estado interno , tampoco pregunta si tiene un estado interno .

¡También diga, no pregunte! no no implicaría no conseguir un resultado en forma de un valor de retorno (proporcionado por una returndeclaración dentro del método). Simplemente implica que no me importa cómo lo haces, ¡pero haz ese procesamiento! . Y a veces quieres inmediatamente el resultado del procesamiento ...


1
Sin embargo, CQS implicaría que la modificación del estado y la obtención de resultados deberían separarse
jk.

77
@jk. Como de costumbre: en general, debe separar el cambio de estado y devolver un resultado, pero en casos excepcionales hay razones válidas para combinarlo. Por ejemplo: un next()método de iteradores no solo debe devolver el objeto actual sino también cambiar el estado interno de los iteradores para que la próxima llamada devuelva el siguiente objeto ...
Timothy Truckle

44
Exactamente. Creo que el problema de OP se debe simplemente a malentendidos / aplicación incorrecta de "decir, no preguntar". Y arreglar ese malentendido hace que el problema desaparezca.
Konrad Rudolph

@KonradRudolph Plus, no creo que sea el único malentendido aquí. Su descripción de una "función pura" incluye "Su estado se establece solo una vez". Otro comentario indica que podría significar el contexto de un cierre, pero la redacción me parece extraña.
Izkata

17

Si considera return"dañino" (permanecer en su imagen), entonces en lugar de hacer una función como

ResultType f(InputType inputValue)
{
     // ...
     return result;
}

compilarlo de manera que pase mensajes:

void f(InputType inputValue, Action<ResultType> g)
{
     // ...
     g(result);
}

Mientras f, y gson libre de efectos secundarios, encadenar juntos será libre de efectos secundarios también. Creo que este estilo es similar a lo que también se llama estilo de paso continuo .

Si esto realmente conduce a programas "mejores" es discutible, ya que rompe algunas convenciones. El ingeniero de software alemán Ralf Westphal hizo todo un modelo de programación en torno a esto, lo llamó "Componentes basados ​​en eventos" con una técnica de modelado que llama "Diseño de flujo".

Para ver algunos ejemplos, comience en la sección "Traducir a eventos" de esta entrada de blog . Para el enfoque completo, recomiendo su libro electrónico "Mensajería como modelo de programación - Hacer POO como si lo quisieras" .


24
If this really leads to "better" programs is debatableSolo tenemos que mirar el código escrito en JavaScript durante la primera década de este siglo. Jquery y sus complementos eran propensos a este paradigma de devoluciones de llamada ... devoluciones de llamada en todas partes . En cierto punto, demasiadas devoluciones de llamadas anidadas , hicieron que la depuración fuera una pesadilla. El código aún debe ser leído por los humanos, independientemente de las excentricidades de la ingeniería del software y sus "principios"
Laiv

1
incluso entonces, en algún momento, debe proporcionar una acción que realice un efecto secundario o necesita alguna forma de regresar de la parte CPS
jk.

12
@Laiv CPS se inventó como una tecnología de optimización para compiladores, en realidad nadie esperaba que los programadores escribieran el código de esta manera a mano.
Joker_vD

3
@CandiedOrange En forma de eslogan, "regresar es simplemente decirle a la continuación qué hacer". De hecho, la creación de Scheme fue motivada al tratar de entender el modelo de actor de Hewitt y llegó a la conclusión de que los actores y los cierres eran lo mismo.
Derek Elkins

2
Hipotéticamente, claro, podría escribir toda su aplicación como una serie de llamadas a funciones que no devuelven nada. Si no le importa que esté estrechamente acoplado, ni siquiera necesita los puertos. Pero la mayoría de las aplicaciones sensatas utilizan funciones que devuelven cosas porque ... bueno, son sensatas. Y como creo que he demostrado adecuadamente en mi respuesta, puede obtener returndatos de las funciones y seguir adhiriéndose a Tell Don't Ask, siempre que no controle el estado de un objeto.
Robert Harvey

7

La transmisión de mensajes es inherentemente efectiva. Si le dice a un objeto que haga algo, espera que tenga un efecto en algo. Si el controlador de mensajes era puro, no necesitaría enviarle un mensaje.

En los sistemas de actores distribuidos, el resultado de una operación generalmente se envía como un mensaje al remitente de la solicitud original. El remitente del mensaje está implícitamente disponible por el tiempo de ejecución del actor o (por convención) se pasa explícitamente como parte del mensaje. Al pasar mensajes sincrónicos, una sola respuesta es similar a una returndeclaración. En el paso de mensajes asíncronos, el uso de mensajes de respuesta es particularmente útil, ya que permite el procesamiento concurrente en múltiples actores sin dejar de ofrecer resultados.

Pasar el "remitente" al que se debe entregar el resultado explícitamente básicamente modela el estilo de paso de continuación o los parámetros temidos, excepto que les pasa mensajes a ellos en lugar de mutarlos directamente.


5

Toda esta pregunta me parece una 'violación de nivel'.

Tiene (al menos) los siguientes niveles en un proyecto importante:

  • El nivel del sistema, por ejemplo, plataforma de comercio electrónico
  • El nivel del subsistema, por ejemplo, validación del usuario: servidor, AD, front-end
  • El nivel de programa individual, por ejemplo, uno de los componentes de lo anterior.
  • El nivel de actor / módulo [esto se vuelve turbio dependiendo del idioma]
  • El método / nivel de función.

Y así sucesivamente hasta fichas individuales.

Realmente no hay necesidad de que una entidad en el nivel de método / función no regrese (incluso si solo regresa this). Y no hay (en su descripción) ninguna necesidad de que una entidad en el nivel de Actor devuelva nada (dependiendo del lenguaje que ni siquiera sea posible). Creo que la confusión está en combinar esos dos niveles, y diría que deberían razonarse claramente (incluso si cualquier objeto dado realmente abarca múltiples niveles).


2

Usted menciona que desea ajustarse tanto al principio OOP de "decir, no preguntar" como al principio funcional de las funciones puras, pero no entiendo cómo eso lo llevó a evitar la declaración de devolución.

Una forma alternativa relativamente común de seguir estos dos principios es ir todo en las declaraciones de retorno y usar objetos inmutables solo con getters. El enfoque es que algunos de los captadores devuelvan un objeto similar con un nuevo estado, en lugar de cambiar el estado del objeto original.

Un ejemplo de este enfoque está en los tipos de datos tupley Python incorporados frozenset. Aquí hay un uso típico de un conjunto congelado:

small_digits = frozenset([0, 1, 2, 3, 4])
big_digits = frozenset([5, 6, 7, 8, 9])
all_digits = small_digits.union(big_digits)

print("small:", small_digits)
print("big:", big_digits)
print("all:", all_digits)

Lo que imprimirá lo siguiente, demostrando que el método de unión crea un nuevo conjunto congelado con su propio estado sin afectar los objetos antiguos:

pequeño: conjunto congelado ({0, 1, 2, 3, 4})

grande: conjunto congelado ({5, 6, 7, 8, 9})

todos: congelados ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

Otro extenso ejemplo de estructuras de datos inmutables similares es la biblioteca Immutable.js de Facebook . En ambos casos, comienza con estos bloques de construcción y puede construir objetos de dominio de nivel superior que siguen los mismos principios, logrando un enfoque OOP funcional, que lo ayuda a encapsular los datos y razonar sobre ellos más fácilmente. Y la inmutabilidad también le permite obtener el beneficio de poder compartir dichos objetos entre hilos sin tener que preocuparse por los bloqueos.


1

He llegado a sospechar que un código bien escrito puede seguir los principios OO y los principios funcionales al mismo tiempo. Estoy tratando de conciliar estas ideas y el gran punto de fricción en el que he aterrizado es el retorno.

He estado haciendo todo lo posible para conciliar algunos de los beneficios de, más específicamente, la programación imperativa y funcional (naturalmente, no obteniendo todos los beneficios, pero tratando de obtener la mayor parte de ambos), aunque en returnrealidad es fundamental para hacerlo en Una manera sencilla para mí en muchos casos.

Con respecto a tratar de evitar returndeclaraciones directamente, traté de reflexionar sobre esto durante la última hora más o menos y básicamente la pila desbordó mi cerebro varias veces. Puedo ver su atractivo en términos de imponer el nivel más fuerte de encapsulación y ocultación de información a favor de objetos muy autónomos a los que simplemente se les dice qué hacer, y me gusta explorar las extremidades de las ideas aunque solo sea para tratar de obtener un mejor comprensión de cómo funcionan.

Si utilizamos el ejemplo del semáforo, inmediatamente un intento ingenuo querría dar a ese semáforo un conocimiento del mundo entero que lo rodea, y eso ciertamente sería indeseable desde una perspectiva de acoplamiento. Entonces, si entiendo correctamente, lo abstrae y desacopla a favor de generalizar el concepto de puertos de E / S que propagan aún más los mensajes y las solicitudes, no los datos, a través de la tubería, y básicamente inyectan estos objetos con las interacciones / solicitudes deseadas entre sí mientras ajenos el uno al otro.

La tubería nodal

ingrese la descripción de la imagen aquí

Y ese diagrama es casi tan lejos como traté de esbozar esto (y aunque simple, tuve que seguir cambiándolo y repensándolo). Inmediatamente tiendo a pensar que un diseño con este nivel de desacoplamiento y abstracción se volvería muy difícil de razonar en forma de código, porque el orquestador (es) que conecta todas estas cosas para un mundo complejo podría encontrarlo muy difícil. realice un seguimiento de todas estas interacciones y solicitudes para crear la canalización deseada. Sin embargo, en forma visual, podría ser razonablemente sencillo dibujar estas cosas como un gráfico y vincular todo y ver que las cosas suceden de manera interactiva.

En términos de efectos secundarios, podría ver que esto está libre de "efectos secundarios" en el sentido de que estas solicitudes podrían, en la pila de llamadas, conducir a una cadena de comandos para que cada hilo se ejecute, por ejemplo (no cuento esto como un "efecto secundario" en un sentido pragmático, ya que no está alterando ningún estado relevante para el mundo exterior hasta que dichos comandos se ejecuten realmente; el objetivo práctico para mí en la mayoría de los programas no es eliminar los efectos secundarios, sino diferirlos y centralizarlos) . Y, además, la ejecución del comando podría generar un mundo nuevo en lugar de mutar el existente. Sin embargo, mi cerebro está realmente agotado al tratar de comprender todo esto, sin ningún intento de crear prototipos de estas ideas. Yo tampoco

Cómo funciona

Entonces, para aclarar, estaba imaginando cómo se programa realmente esto. La forma en que lo veía funcionar era en realidad el diagrama anterior que capturaba el flujo de trabajo del usuario final (programador). Puede arrastrar un semáforo al mundo, arrastrar un temporizador, darle un período transcurrido (al "construirlo"). El temporizador tiene un On Intervalevento (puerto de salida), puede conectarlo al semáforo para que, en tales eventos, le indique a la luz que pase por sus colores.

El semáforo podría entonces, al cambiar a ciertos colores, emitir salidas (eventos) como, On Reden ese punto, podríamos arrastrar a un peatón a nuestro mundo y hacer que ese evento le diga al peatón que comience a caminar ... o podríamos arrastrar pájaros hacia nuestra escena y hacerlo así, cuando la luz se pone roja, les decimos a las aves que comiencen a volar y batir sus alas ... o tal vez cuando la luz se pone roja, le decimos a una bomba que explote, lo que queramos, y con los objetos siendo completamente ajenos el uno al otro, y sin hacer nada más que indirectamente diciéndose mutuamente qué hacer a través de este concepto abstracto de entrada / salida.

Y encapsulan completamente su estado y no revelan nada al respecto (a menos que estos "eventos" se consideren TMI, en ese punto tendré que repensar mucho las cosas), se dicen mutuamente cosas que deben hacer indirectamente, no preguntan. Y están súper desacoplados. Nada sabe nada excepto esta abstracción generalizada de puertos de entrada / salida.

Casos de uso práctico?

Pude ver que este tipo de cosas son útiles como un lenguaje incrustado específico de dominio de alto nivel en ciertos dominios para orquestar todos estos objetos autónomos que no saben nada sobre el mundo circundante, no exponen nada de su estado interno posterior a la construcción, y básicamente solo propagan solicitudes entre nosotros que podemos cambiar y ajustar al contenido de nuestros corazones. En este momento siento que esto es muy específico del dominio, o tal vez simplemente no he pensado lo suficiente, porque es muy difícil para mí pensar en los tipos de cosas que desarrollo regularmente (a menudo trabajo con código de nivel medio bajo) si tuviera que interpretar Tell, Don't Ask a tales extremos y quisiera el nivel más fuerte de encapsulación imaginable. Pero si estamos trabajando con abstracciones de alto nivel en un dominio específico,

Señales y Ranuras

Este diseño me pareció extrañamente familiar hasta que me di cuenta de que son básicamente señales y ranuras si no tomamos en cuenta los matices de cómo se implementa. La pregunta principal para mí es qué tan efectivamente podemos programar estos nodos individuales (objetos) en el gráfico como estrictamente adheridos a Tell, Don't Ask, llevados al grado de evitar returndeclaraciones, y si podemos evaluar dicho gráfico sin mutaciones (en paralelo, por ejemplo, bloqueo ausente). Ahí es donde están los beneficios mágicos no en cómo conectamos estas cosas potencialmente, sino en cómo se pueden implementar a este grado de mutaciones ausentes de encapsulación. Ambos me parecen factibles, pero no estoy seguro de cuán ampliamente aplicable sería, y ahí es donde estoy un poco perplejo tratando de resolver posibles casos de uso.


0

Claramente veo fuga de certeza aquí. Parece que "efecto secundario" es un término bien conocido y comúnmente entendido, pero en realidad no lo es. Dependiendo de sus definiciones (que en realidad faltan en el OP), los efectos secundarios pueden ser totalmente necesarios (como @JacquesB logró explicar), o ser aceptados sin piedad. O, dando un paso hacia la aclaración, es necesario distinguir entre los efectos secundarios deseados que a uno no le gusta ocultar (en este punto emerge el famoso IO de Haskell: no es más que una forma de ser explícito) y los efectos secundarios no deseados como un resultado de errores de código y ese tipo de cosas . Esos son problemas bastante diferentes y, por lo tanto, requieren un razonamiento diferente.

Por lo tanto, sugiero comenzar a reformular: "¿Cómo definimos el efecto secundario y qué dicen las definiciones dadas sobre su interrelación con la declaración" return "?".


1
"en este punto emerge el famoso IO de Haskell: no es más que una forma de ser explícito" - la explicidad es ciertamente un beneficio del IO monádico de Haskell, pero hay otro punto: proporciona un medio para aislar el efecto secundario para ser completamente fuera del lenguaje: aunque las implementaciones comunes en realidad no hacen esto, principalmente debido a problemas de eficiencia, conceptualmente es cierto: la mónada IO puede considerarse como una forma de devolver una instrucción a un entorno que está completamente fuera del entorno. Programa Haskell, junto con una referencia a una función para continuar después de la finalización.
Julio
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.