¿Son útiles las continuaciones de primera clase en los modernos lenguajes de programación orientados a objetos?


10

Las continuaciones son extremadamente útiles en lenguajes de programación funcionales (por ejemplo, la Contmónada en Haskell) porque permiten una notación simple y regular para el código de estilo imperativo. También son útiles en algunos idiomas imperativos más antiguos porque se pueden usar para implementar características de lenguaje faltantes (por ejemplo, excepciones, corutinas, hilos verdes). Pero para un lenguaje orientado a objetos moderna con soporte incorporado para estas características, ¿qué argumentos habría para añadir también soporte para las continuaciones de primera clase (si el estilo delimitado más moderno resety shifto esquema similar call-with-current-continuation)?

¿Existen argumentos en contra de agregar soporte además del rendimiento y la complejidad de la implementación?

Respuestas:


15

Dejemos que Eric Lippert responda a esta:

La pregunta obvia en este punto es: si CPS es tan increíble, ¿por qué no lo usamos todo el tiempo? ¿Por qué la mayoría de los desarrolladores profesionales nunca han oído hablar de él, o aquellos que lo han hecho, piensan que es algo que solo hacen esos locos programadores de Scheme?

En primer lugar, es simplemente difícil para la mayoría de las personas que están acostumbradas a pensar en subrutinas, bucles, try-catch-finally y demás para razonar sobre los delegados que se utilizan para controlar el flujo de esta manera. Estoy revisando mis notas sobre CPS de CS442 en este momento y veo que en 1995 escribí un profQUOTE: "Con las continuas tienes que pararte de cabeza y tirarte". El profesor Duggan (*) tenía toda la razón al decir eso. Recordemos hace un par de días que nuestro pequeño ejemplo de transformación CPS de la expresión C # M (B ()? C (): D ()) involucraba cuatro lambdas. No todos son buenos para leer códigos que usan funciones de orden superior. Impone una gran carga cognitiva.

Además, una de las cosas buenas de tener declaraciones de flujo de control específicas integradas en un idioma es que permiten que su código exprese claramente el significado del flujo de control al tiempo que oculta los mecanismos: las pilas de llamadas y las direcciones de retorno y las listas de controladores de excepciones y regiones protegidas y así. Las continuaciones hacen explícitos los mecanismos de flujo de control en la estructura del código. Todo ese énfasis en el mecanismo puede abrumar el significado del código.

En el siguiente artículo , explica cómo la asincronía y las continuaciones son exactamente equivalentes entre sí, y repasa una demostración de tomar una operación de red síncrona simple (pero bloqueante) y reescribirla en un estilo asíncrono, asegurándose de cubrir todo lo oculto gotchas que deben cubrirse para hacerlo bien. Se convierte en un desorden masivo de código. Su resumen al final:

Santo cielo, qué desastre tan horrible hemos hecho. Hemos expandido dos líneas de código perfectamente claro en dos docenas de los espaguetis más deliciosos que hayas visto. Y, por supuesto, ni siquiera se compila porque las etiquetas no están dentro del alcance y tenemos un error de asignación definitivo. Todavía tendríamos que volver a escribir el código para solucionar esos problemas.

¿Recuerdas lo que decía sobre los pros y los contras de CPS?

  • PRO: Los flujos de control arbitrariamente complejos e interesantes pueden construirse a partir de partes simples: verificar.
  • CON: La reificación del flujo de control a través de continuaciones es difícil de leer y de razonar: verifique.
  • CON: El código que representa los mecanismos de flujo de control abruma por completo el significado del código: verificar.
  • CON: La transformación del flujo de control de código ordinario en CPS es el tipo de cosas en las que los compiladores son buenos, y casi nadie más lo es - verifique.

Este no es un ejercicio intelectual. Las personas reales terminan escribiendo código moralmente equivalente al anterior todo el tiempo cuando tratan con la asincronía. E, irónicamente, a pesar de que los procesadores se han vuelto más rápidos y más baratos, pasamos cada vez más tiempo esperando cosas que no estén vinculadas al procesador. En muchos programas, gran parte del tiempo empleado es con el procesador vinculado a cero esperando que los paquetes de red realicen el viaje de Inglaterra a Japón y viceversa, o que los discos giren, o lo que sea.

Y ni siquiera he hablado de lo que sucede si quieres componer más operaciones asincrónicas. Suponga que desea hacer ArchiveDocuments una operación asincrónica que toma un delegado. Ahora todo el código que lo llama también debe escribirse en CPS. La mancha simplemente se extiende.

Lograr que la lógica asincrónica sea correcta es importante, solo será más importante en el futuro, y las herramientas que le hemos dado lo hacen "ponerse de cabeza y volverse del revés", como sabiamente dijo el profesor Duggan.

El estilo de aprobación continua es poderoso, sí, pero tiene que haber una mejor manera de hacer un buen uso de ese poder que el código anterior.

Vale la pena leer ambos artículos, y la siguiente serie sobre una nueva característica del lenguaje C # que mueve todo este desorden al compilador y le permite escribir su código como flujo de control normal con una palabra clave especial para marcar ciertas partes como asíncronas. No eres un desarrollador de C #. No lo soy, pero aún así fue una experiencia muy esclarecedora cuando la encontré por primera vez.


55
Vale la pena señalar que si su idioma admite extensiones de sintaxis como las macros, las continuaciones se vuelven mucho más útiles porque puede ocultar los mecanismos para implementar varios tipos de flujo de control interesante dentro de las extensiones de sintaxis. Que es básicamente cómo funciona "esperar" de todos modos, solo se define en el lenguaje ya que C # no proporciona las herramientas para definir ese tipo de extensiones de sintaxis.
Jack

Buen artículo, aunque notaré que CPS y las continuaciones de primera clase no son exactamente lo mismo (CPS es lo que haces en un lenguaje sin soporte para las continuaciones cuando descubres que las necesitas). Parece que C # está yendo exactamente por la misma ruta en la que estoy pensando (aunque no puedo decidir si quiero automatizar la transformación de neumáticos a CPS o implementar continuaciones delimitadas de copia completa de la pila).
Jules

@Jack: eso es exactamente lo que estoy considerando en este momento: el lenguaje que estoy diseñando tiene una función de macro que podría admitir la transformación CPS automáticamente. Pero las continuaciones nativas completas podrían dar un mejor rendimiento ... la pregunta es, ¿con qué frecuencia se usarían en una situación crítica de rendimiento?
Jules
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.