Lo que esencialmente está preguntando es la diferencia entre el poder computacional y lo que comúnmente se llama poder expresivo (o simplemente expresividad ) de un lenguaje (o sistema de cómputo).
Potencia de cálculo
El poder computacional se refiere a qué tipos de problemas puede calcular el lenguaje. La clase de potencia computacional más conocida es la que es equivalente a una máquina universal de Turing . Hay una gran cantidad de otros sistemas de computación, tales como acceso aleatorio Máquinas , λ-cálculo , SK cálculo combinador , funciones mu-recursivo , WHILE
programas, y muchos otros. Y resulta que todos estos pueden simularse entre sí, lo que significa que todos tienen el mismo poder computacional.
Esto da lugar a la Tesis de Church-Turing (llamada así por Alonzo Church que creó el cálculo λ y Alan Turing que creó la Máquina Universal de Turing). La tesis de la Iglesia de Turing es una hipótesis sobre la computabilidad con dos aspectos:
- todos los sistemas de computación capaces de computación general son igualmente poderosos y
- un ser humano que sigue un algoritmo puede calcular exactamente las funciones que una máquina de Turing (y, por lo tanto, cualquiera de los otros sistemas) puede calcular.
Sin embargo, el segundo es más importante en el campo de la filosofía de la mente que la informática.
Sin embargo, hay dos cosas que la Tesis de Turing de la Iglesia no dice, que son muy relevantes para su pregunta:
- qué tan eficientes son las diversas simulaciones y
- cuán conveniente es la codificación de un problema.
Un ejemplo simple para (1): en una máquina de acceso aleatorio, copiar una matriz requiere un tiempo proporcional a la longitud de la matriz. Sin embargo, en una máquina de Turing, lleva un tiempo proporcional al cuadrado de la longitud de la matriz, porque la máquina de Turing no tiene acceso aleatorio a la memoria, solo puede moverse a través de la cinta una celda a la vez. Por lo tanto, necesita moverse a través de los n elementos de la matriz n veces para copiarlos. Por lo tanto, diferentes modelos de cálculo pueden tener diferentes características de rendimiento, incluso en el caso asintótico, donde tratamos de abstraernos de los detalles de implementación.
Abundan los ejemplos para (2): tanto el cálculo λ como Python son completos de Turing. ¿Pero prefieres escribir un programa en Python o en cálculo λ?
También hay una tercera arruga que he eludido hasta ahora: todos esos sistemas originales fueron diseñados por lógicos, filósofos o matemáticos, no por informáticos ... simplemente porque las computadoras y, por lo tanto, la informática no existían. Todo esto se remonta a principios de la década de 1930, incluso antes de los primeros experimentos de Konrad Zuse (que de todos modos no eran programables y / o Turing completos). Solo hablan de "funciones computables en los números naturales".
Ahora, como resultado, hay muchas cosas que puedes expresar como funciones en números naturales; después de todo, nuestras computadoras modernas incluso funcionan con mucho menos que eso (básicamente 3-4 funciones en los números 0 y 1, y eso es todo ), pero, por ejemplo, ¿qué función calcula un sistema operativo?
Esta noción de E / S, efectos secundarios, interactuando con el medio ambiente, no está captada por la idea de "funciones sobre números naturales". Y, sin embargo, es algo importante, ya que, como Simon Peyton Jones dijo una vez: "Todo lo que hace una función pura sin efectos secundarios es calentar tu CPU" , a lo que un miembro de la audiencia respondió "en realidad, eso es un lado -efecto, también! "
Edwin Brady , el diseñador de Idris , (solo la mitad) usa en broma (no sé si lo inventó) el término "Tetris-complete" para expresar esta diferencia entre "puede calcular cualquier función computable en números naturales" y "puede ser usado para escribir programas no triviales que interactúan con el entorno ". Aún más irónicamente, demuestra esto al implementar un clon de Space Invaders en Idris , pero dice que confía en que Tetris se reduce a Space Invaders.
Otra cosa a destacar es que no solo la equivalencia de Turing no es necesariamente suficiente para hablar sobre la escritura de programas "útiles", sino que puede que OTOH ni siquiera sea necesario . Por ejemplo, SQL solo se ha convertido en equivalente de Turing con ANSI SQL: 1999 , pero aún era útil antes de eso. De hecho, algunos podrían argumentar que hacer que sea equivalente a Turing no ha aumentado su utilidad en absoluto. Hay muchos lenguajes específicos de dominio que no son equivalentes a Turing. El lenguaje de descripción de datos generalmente no es (y no debería ser). Total Languages obviamente no puede ser equivalente a Turing, pero aún puede escribir bucles de eventos, servidores web o sistemas operativos en ellos. También hay idiomas que son equivalentes a Turing pero que en realidad se consideran un error.
Entonces, en general, la equivalencia de Turing no es terriblemente interesante, a menos que desee analizar estáticamente los programas.
Expresividad
Suponiendo que nuestro sistema de computación es computacionalmente lo suficientemente poderoso como para resolver nuestro problema, lo que tenemos que hacer a continuación es expresar nuestro algoritmo para resolver ese problema en algún tipo de notación formal para ese sistema. En otras palabras: necesitamos escribir un programa en algún lenguaje de computadora. Ahí es donde entra en juego la noción de expresividad .
Se refiere esencialmente a lo "fácil" o "agradable" que es escribir nuestro programa en nuestro lenguaje de programación particular. Como puede ver, la noción es bastante vaga, subjetiva y más psicológica que técnica.
Sin embargo, hay intentos de definiciones más precisas. El más famoso (y el más riguroso que conozco) es de Matthias Felleisen en su artículo Sobre el poder expresivo de los lenguajes de programación (las dos primeras páginas contienen una introducción suave, el resto del documento es más carnoso).
La intuición principal es la siguiente: cuando se traduce un programa de un idioma a otro idioma, algunos de los cambios que necesita realizar están contenidos localmente (como, por ejemplo, convertir FOR
bucles en WHILE
bucles o bucles en GOTO
s condicionales ), y algunos requieren un cambio a lo global estructura del programa.
Cuando puede reemplazar una característica de un idioma con una característica diferente de un idioma diferente por solo transformaciones locales, entonces se dice que estas características no tienen efecto sobre el poder expresivo. Esto se llama azúcar sintáctico .
Por otro lado, si requiere un cambio en la estructura global del programa, se dice que el idioma al que está traduciendo no puede expresar la función. Y se dice que el lenguaje del que está traduciendo es más expresivo (con respecto a esta característica).
Tenga en cuenta que esto proporciona una definición de expresividad objetivamente medible. Tenga en cuenta también que la noción depende del contexto de la característica, y es comparativa. Entonces, si todos los programas en el idioma A se pueden traducir al idioma B con solo cambios locales, y hay al menos un programa en el idioma B que no se puede traducir a A con solo cambios locales, entonces el idioma B es estrictamente más expresivo que el idioma UNA. Sin embargo, el escenario más probable es que muchos programas en ambos idiomas se pueden traducir de un lado a otro, pero hay algunos programas en ambos idiomas que no se pueden traducir al otro. Esto significa que ninguno de los idiomas es estrictamente más expresivo que el otro, solo tienen características diferentes que permiten que diferentes programas se expresen de diferentes maneras.
Esto proporciona una definición formal de lo que significa ser "más expresivo", pero aún no capta las nociones psicológicas detrás del fenómeno. Por ejemplo, el azúcar sintáctico, de acuerdo con este modelo, no aumenta el poder expresivo de un idioma, ya que puede traducirse utilizando solo cambios locales. Sin embargo, sabemos por experiencia que tienen FOR
, WHILE
y IF
disponible, incluso si son de azúcar sintáctica para simplemente condicionales GOTO
marcas expresar nuestra intención más fácil .
El hecho es que diferentes idiomas tienen características diferentes que hacen que expresar diferentes formas de pensar sobre un problema sea más fácil o más difícil. Y algunas personas pueden encontrar una manera de expresar su intención más fácilmente y otras una manera diferente.
Un ejemplo que encontré en la etiqueta Ruby en StackOverflow: muchos usuarios que siguen la etiqueta Ruby afirman que los bucles son más fáciles de entender que la recursividad y la recursión es solo para programadores funcionales avanzados y los bucles son más intuitivos para los recién llegados, pero he visto múltiples casos de recién llegados que intuitivamente escriben código como este:
def rock_paper_scissors
get_user_input
determine_outcome
print_winner
rock_paper_scissors # start from the top
end
Lo que generalmente lleva a varias personas a comentar que "esto no funciona" y "lo están haciendo mal" y la "forma correcta" es esta:
def rock_paper_scissors
loop do
get_user_input
determine_outcome
print_winner
end
end
Entonces, claramente, hay algunas personas para quienes la recursividad de la cola es una forma más natural de expresar el concepto de "bucle" que las construcciones de bucle.
Resumen
El hecho de que dos idiomas sean equivalentes a Turing dice una y exactamente una cosa: que pueden calcular el mismo conjunto de funciones en números naturales que una máquina de Turing. Eso es.
No dice nada acerca de qué tan rápido calculan esas funciones. No dice nada sobre la facilidad de expresar esas funciones. Y no dice nada acerca de qué más pueden hacer además de calcular funciones en números naturales (por ejemplo, vincular a bibliotecas C, leer la entrada del usuario, escribir la salida en la pantalla).
¿eso significa que la clase de problemas que cada lenguaje de programación puede resolver realmente varía según el idioma, a pesar de que estos lenguajes están completos?
Sí.
- Hay problemas que no están cubiertos por el término "Turing-complete" (que solo se refiere a las funciones informáticas en números naturales) como la impresión en la pantalla. Se pueden completar dos idiomas en Turing, pero uno puede permitir la impresión en la pantalla y el otro no.
- Incluso si ambos idiomas pueden resolver los mismos problemas, eso no dice nada sobre cuán compleja es la codificación y lo fácil que es expresar esta codificación. Por ejemplo, C puede resolver todos los problemas que Haskell puede, simplemente escribiendo un intérprete de Haskell en C ... ¡pero primero debe escribir el intérprete de Haskell para resolver un problema de esta manera!