KISS ("mantenerlo simple, estúpido" o "mantenerlo simple estúpido", ver, por ejemplo, aquí ) es un principio importante en el desarrollo de software, aunque aparentemente se originó en la ingeniería. Citando del artículo de Wikipedia:
El principio se ejemplifica mejor con la historia de Johnson entregando a un equipo de ingenieros de diseño un puñado de herramientas, con el desafío de que el avión a reacción que estaban diseñando debe ser reparable por un mecánico promedio en el campo en condiciones de combate con solo estas herramientas. Por lo tanto, lo 'estúpido' se refiere a la relación entre la forma en que las cosas se rompen y la sofisticación disponible para arreglarlas.
Si quisiera aplicar esto al campo del desarrollo de software, reemplazaría "avión a reacción" con "pieza de software", "mecánico promedio" con "desarrollador promedio" y "en condiciones de combate" con "bajo el desarrollo / mantenimiento de software esperado condiciones "(plazos, limitaciones de tiempo, reuniones / interrupciones, herramientas disponibles, etc.).
Por lo tanto, es una idea comúnmente aceptada que uno debe tratar de mantener una pieza de software simple (o simple estúpida , en caso de que omita la coma) para que sea fácil trabajar en ella más tarde.
Pero, ¿puede aplicarse el principio KISS también al diseño del lenguaje de programación? ¿Conoce algún lenguaje de programación que haya sido diseñado específicamente con este principio en mente, es decir, para "permitir que un programador promedio en condiciones de trabajo promedio escriba y mantenga tanto código como sea posible con el menor esfuerzo cognitivo"?
Si cita algún idioma específico, sería genial si pudiera agregar un enlace a algún documento en el que los diseñadores del lenguaje expresen claramente esta intención. En cualquier caso, me interesaría conocer las intenciones (documentadas) de los diseñadores en lugar de su opinión personal sobre un lenguaje de programación en particular.
Respuestas:
Cuando pienso en el minimalismo, pienso en Lisp y Go . Con Lisp, todo lo que tiene son funciones y listas, que es lo más simple que puede obtener (bueno, hay un poco más, pero lo que sea). Sin embargo, creo que el caso Go es más interesante.
Go fue diseñado para ser simple (es una lectura decente). El término que utilizan es "ortogonalidad de características", lo que significa que cualquier característica solo debe agregarse si proporciona algo realmente único. Esto parece surgir con la participación de los autores ( Russ Cox y Rob Pike vienen a mi mente) con Plan9 , que fue una reinvención de UNIX con la simplicidad en mente. (Si está interesado en un diseño minimalista, el artículo de Rob Pike sobre un sistema simple de ventanas es una buena lectura).
Aquí hay algunos ejemplos simples de la sintaxis:
Solo una construcción en bucle
Un bucle puede verse como uno de los siguientes:
Bucle infinito
Mientras bucle
Tradicional para loop
Bucle foreach
Interruptor multiusos
Retorno múltiple
Interfaces
Incrustación
Canales
Se puede usar para implementar semáforos
Se usa para el mensaje que pasa entre hilos
Se puede usar para manejar eventos asincrónicos
Conclusión
No voy a entrar en cada parte de la sintaxis, pero espero que puedan ver lo que puede hacer el minimalismo. El lenguaje es intrigante no porque agrega un montón de nuevas características, sino porque usa las mejores características de otros idiomas sin nada adicional.
Generalmente hay una "mejor" forma de resolver un problema. Por ejemplo, en la lista de correo, muchos usuarios se quejan de no tener genéricos. Después de la discusión, se dan cuenta de que todo lo que quieren hacer se puede hacer con interfaces. Lea sobre ir efectivo para obtener ejemplos de sintaxis idiomática.
El beneficio de los idiomas de KISS es que es posible escribir código idiomático, porque el estilo de código está restringido por el idioma. Por ejemplo, en Go, no puedes escribir algo como esto:
Tienes que usar llaves:
Hay muchos otros ejemplos de esto en la sintaxis, lo que facilita la lectura del código de otras personas.
Beneficios del lenguaje KISS sobre los lenguajes característicos:
fuente
for i=1 to 10
o python:for item in group
for i = 1 to 10
es sii
alguna vez será 10. Esto puede depender del idioma (bash incluye 10, python no). El bucle for tradicional es universal.Creo que el Zen de Python indicará por qué Python es un lenguaje simple mucho mejor de lo que puedo:
Editar en respuesta a @Giorgio
Como dice tu pregunta,
Python es, para mí, lo que inmediatamente me viene a la mente. El diseño de Python responde directamente a la metodología de Perl, el famoso "Hay más de una forma de hacerlo". Si bien eso es genial, ya que permite a los programadores escribir código muy fácilmente, no ayuda con el mantenimiento. Habiendo administrado antes un programa (muy mal escrito, lo admito) escrito en Perl, aprecio que Python fuerce tanto buenas prácticas de codificación como un lenguaje conciso.
Python tampoco lo obliga a seguir una metodología particular al escribir programas. Puedo elegir seguir un estricto estilo de programación orientado a objetos, o puedo escribir un script simple para ejecutar de forma secuencial. No tener que inicializar una clase, luego llamar al
main()
método como se ve obligado a hacerlo en Java es muy bueno. (Además, imprimir en stdout en Python es maravilloso, pero ese es un punto bastante nulo).Finalmente, la metodología "Baterías incluidas" de incluir una biblioteca estándar expansiva con el lenguaje es maravillosa. En lugar de buscar en algún repositorio externo de paquetes, la mayoría de lo que necesito ya está incluido en el lenguaje. También es bueno tener ese repositorio externo, pero no tener que buscarlo para hacer una operación básica es realmente útil.
fuente
import this
comando.Una clave importante para mantener la "simplicidad" es reconocer cuándo será necesaria la complejidad y hacer que las partes del sistema que mejor puedan manejarla lo hagan. Tener un solo tipo de referencia de objeto que no hace distinción entre valores inmutables, valores mutables y entidades, hace que Java sea "simple", no elimina la necesidad de distinguir entre valores y entidades; simplemente priva a los programadores de herramientas para ayudarlos a hacerlo.
En términos más generales, si se intenta que un lenguaje de programación o una función de marco admitan múltiples casos de uso, se debe garantizar que no haya situaciones en las que diferentes comportamientos sean apropiados en diferentes casos de uso, pero el compilador no podrá determinar ellos aparte. Agregar más tipos a un lenguaje o marco puede parecer una complicación, pero en muchos casos en realidad puede facilitar las cosas.
Considere, por ejemplo, el desorden de las reglas que rodean los tipos con signo y sin signo en C. La complejidad de estas reglas se debe al hecho de que algunos códigos usan tipos enteros sin signo para representar números, mientras que otros códigos los usan para representar miembros de un anillo algebraico envolvente (específicamente, el conjunto de enteros congruentes mod 2ⁿ). Las reglas de conversión de tipos se comportan de maneras que a veces son apropiadas para un uso, y a veces de maneras apropiadas para el otro. Tener distintos tipos de envoltura y no envoltura duplicaría el número de tipos enteros, pero podría simplificar las reglas asociadas con ellos:
Cualquier tipo entero sin anillo de cualquier tamaño puede asignarse a un anillo de cualquier tamaño; un anillo puede asignarse solo a un anillo de igual o menor tamaño.
Las operaciones que no sean operadores relacionales que involucren un número entero sin anillo y un anillo convertirán implícitamente el número entero al tipo de anillo.
Los anillos se pueden lanzar explícitamente a números o a anillos más grandes; El valor de un anillo sin signo es el número entero no negativo más pequeño que, cuando se agrega al cero del anillo, daría el valor del anillo. El valor de un anillo con signo es el entero de menor magnitud que, cuando se agrega al cero del anillo, daría el valor del anillo, prefiriéndose el valor negativo en caso de empate.
Excepto como se mencionó anteriormente, los anillos están limitados a operaciones con anillos del mismo tamaño o más pequeños.
Las operaciones en enteros que no son de anillo de diferentes tipos deben convertir los números a un tipo que pueda acomodar todos los valores de cualquier operando, o el compilador debe rechazarlos si no existe un tipo adecuado
La definición de los tipos de anillo parecería duplicar el número de tipos enteros, pero simplificaría enormemente la escritura de código portátil. El uso de los mismos tipos sin signo para números y anillos reduce el número de tipos, pero conduce a reglas complicadas que hacen casi imposible escribir código portátil eficiente.
fuente
Podría decirse que Tcl se desarrolló a lo largo de estas líneas. El lenguaje completo se puede describir en solo 12 reglas en una sola página de manual . Es notablemente simple y consistente.
El creador de Tcl afirmó lo siguiente como los objetivos originales:
De " Historia de Tcl " - http://www.tcl.tk/about/history.html
fuente
Si un idioma tiene un diseño fijo debido a KISS, no puede crecer. Un lenguaje que no puede crecer morirá.
En el siguiente video, Guy Steele explica ingeniosamente que se debe permitir que crezca un lenguaje de programación y por qué. Si aplica KISS, entonces, ¿cómo puede crecer un idioma porque una vez lanzado, su conjunto de herramientas es fijo y nunca se le permite cambiar?
Es un video de una hora pero vale la pena verlo. Si no sabes quién es Guy Steele, deberías saberlo cuando hablas del diseño del lenguaje.
Elijo este video como respuesta porque creo que aplicar KISS al diseño del lenguaje en general es incorrecto, y espero que ver un discurso de una persona destacada en el diseño del lenguaje ayude a ampliar su comprensión del futuro del diseño del lenguaje.
Desde que vino aquí para aprender sobre el diseño del lenguaje y le di una razón para no usar KISS, es justo que señale algo que encuentro de ayuda en el diseño del lenguaje. Dimensiones cognitivas de notaciones
EDITAR
Cuando escribí la respuesta anterior, se basó en el razonamiento de: si un motor solo se puede mantener con un conjunto fijo de herramientas, entonces mi reformulación de ese significado con respecto al diseño del lenguaje es que un idioma no puede cambiar una vez lanzado. Los comentarios indican que eso no era lo que se quería decir. Permítanme presentar esta respuesta modificada que estará más en línea con la comprensión revisada.
Si se observan las dimensiones cognitivas de las notaciones , se aprende que hay muchas dimensiones competitivas asociadas con el diseño del lenguaje que la simpleza y si se enfoca demasiado en una, sufrirá en otras. Con la cuestión de centrarse en gran medida en la simplicidad (KISS) buena, y respaldada por personas notables del diseño del lenguaje, presenté el discurso de Guy Steele para mostrar que tratar de mantener un diseño únicamente simple afectará otras dimensiones. Más importante aún, estoy tratando de transmitir que necesitas mirar muchas dimensiones y sopesar los pros y los contras de ellas, no solo la simplicidad.
fuente
Aquí hay una gran cita de Hardcore Visual Basic de Bruce McKinney , que a su vez pone palabras en boca de los diseñadores de BASIC, Kemeny y Kurtz . Destaca el mío.
fuente
Es bueno que haya aclarado lo que quiere decir con "simple", porque en mi opinión, un lenguaje de programación simple es uno con una sintaxis mínima y pocas características (Scheme, Forth, ML), que no se traduce directamente a su definición. Creo que realmente está buscando un diseño de idiomas con RAD (desarrollo rápido de aplicaciones) en mente, y hay bastantes. Vea este hilo de StackOverflow, por ejemplo: /programming/66227/what-is-the-best-multi-platform-rad-language
fuente
Me sorprende que nadie haya mencionado aún C, aunque pone la simplicidad primero de varias maneras importantes, a menudo de una manera tan radical que los programadores no pueden apreciar la simplicidad:
No hay magia oculta. Si escribe
a + b
en C, tiene la garantía de que se compilará como máximo en una sola instrucción de ensamblador.Incluso en lenguajes relativamente simples como Java, una declaración simple como
a + b
puede tomar varios microsegundos (si las variables son cadenas). Otros lenguajes agregan mucho, mucho más a esto con el ejemplo extremo de C ++, dondea + b
puede sobrecargarse para que aparezcan elefantes rosados. No es así en C: si no es una llamada a función, no tomará más de unos pocos nanosegundos. Esta previsibilidad del rendimiento es una de las características clave de C, y ciertamente es una forma de simplicidad.Los tipos de datos son tan simples como pueden ser, al tiempo que describen todas las estructuras de datos interesantes que es posible que desee construir en la memoria: solo hay los tipos fundamentales que una CPU puede manejar + agregación (
struct
) + repetición (matrices) + referencias (punteros )Es cierto que los diversos lenguajes basados en lisp son mucho más simples que C a este respecto. Sin embargo, no intentan permitir que el programador manipule libremente la memoria como lo hace C.
Ortogonalidad de funciones: puede combinar libremente las funciones y funcionarán juntas como se espera. Muchos lenguajes fallan esto al permitir que ciertas construcciones solo aparezcan en contextos definidos.
Tomemos, por ejemplo, matrices de longitud variable en C y C ++: C permite longitudes de tiempo de ejecución en todas partes, mientras que las solicitudes más liberales para expandir el estándar C ++ solo las permiten en ciertos contextos como las matrices automáticas e incluso allí solo en la primera dimensión. Esto permite que C maneje matrices multidimensionales verdaderas con tamaños conocidos solo en tiempo de ejecución, mientras que los programadores de C ++ se reducen para escribir
data[k + (j + i*lineLength)*lineCount]
una y otra vez.Otro ejemplo de esto es el puntero: un puntero de datos en C realmente no es más o menos que una variable que contiene una dirección de memoria de alguna otra variable. Como el puntero es en sí mismo una variable, el puntero doble es una cosa bien definida. Es decir, qué
int
y quéint*
es, está claro lo queint**
debe ser. Compare esto con las referencias de C ++ donde no tiene absolutamente ninguna idea de quéint&&
se le da el significado deint
yint&
!)Es cierto que es precisamente esta simplicidad la que le ha ganado tanto odio a C: la simplicidad del lenguaje permite a los programadores más libertad de la que es buena para ellos, lo que genera muchos problemas con comportamientos indefinidos y punteros mal manejados. Especialmente la simplicidad de la ortogonalidad que permite a un programador definir una variable como
int (*array[m])(struct foo* (*arg)[n])
es del tipo que tiende a causar dolores de cabeza a los lectores porque las cosas realmente complejas se pueden expresar de forma muy concisa mediante una combinación liberal de algunas características simples. . Este es el tipo de simplicidad en el que C sobresale y que le da la reputación de ser un lenguaje difícil de usar.Además, la simplicidad del lenguaje obliga al programador a escribir mucho más código que más lenguajes ricos en funciones, lo que proporciona un terreno más fértil para que florezcan los errores. Sin embargo, sigue siendo el lenguaje de alto nivel más simple posible si su objetivo principal es programar una computadora real y no una máquina virtual.
fuente
Wikipedia en Java : aunque no se establece explícitamente en Wikipedia, la simplificación se menciona varias veces y fue un objetivo de diseño original, ya que el único lenguaje serio capaz de OO (término utilizado libremente) en esos días era C ++, que como todos sabemos, es poderoso pero tiene más de unas pocas trampas para los incautos.
La JVM fue pensada como una forma de simplificar el desarrollo multiplataforma, y "Escribir una vez Ejecutar en cualquier lugar" se convirtió en un mantra de Java durante unos años.
Creo que C # cae en esa categoría de simplificación del lenguaje.
fuente
Hm ... esta es realmente una pregunta difícil de responder 'positivamente', porque cuando pienso en la simplicidad en el diseño del lenguaje de programación (y en qué es 'más fácil' trabajar), inmediatamente pienso en:
Hay varios lenguajes que hacen bien estas cosas, pero lo que me viene a la cabeza es un lenguaje que no hace bien las cosas 'como lenguaje de programación': PHP.
[Descargo de responsabilidad: he escrito PHP y PHP sigue siendo mi 'ir al lenguaje' para proyectos web simples. Esta es una crítica del amor ...]
Primero, PHP funciona bien para el entorno en el que normalmente se ejecuta: servidores web. Es fácil de configurar, fácil de mantener, etc.
Donde lucho con PHP: cuando quieres hacer algo "inusual", algo que normalmente no haces de forma regular (en el caso de que esté pensando que estaba consumiendo un servicio web SOAP), no es algo que yo haya hecho. hecho mucho con PHP), te enfrentas a dos opciones:
1) Varias extensiones de código abierto para PHP. El modelo de objetos PHP es lo suficientemente flexible donde el diseño de la interfaz tampoco es terriblemente consistente de biblioteca a biblioteca, de desarrollador a desarrollador. Cuando te enfrentas a un conjunto de códigos donde alguien usó una biblioteca de la que nunca has oído hablar, pasas mucho tiempo descubriendo "qué diablos hace esto" porque el lenguaje permite la creación de API / biblioteca suelta.
2) Las funciones "incorporadas", de las cuales hay muchas . He pasado por algunas dificultades para encontrar otra biblioteca o implementar un poco de funcionalidad para luego tropezar con
impossiblyfound_basefunction()
En mi opinión, la simplicidad es consistencia.
fuente
Ahora, el enfoque de KISS para los lenguajes de programación es una noción divertida, al menos si considera que los lenguajes de programación son una capa de abstracción para el conjunto de instrucciones de una CPU, etc. Si no define "KISS" más de cerca. Solo digo aquí, que una carreta de bueyes es KISS aplicada a un automóvil al máximo.
Ahora, otra interpretación de KISS podría ser algo así como "hecho inteligentemente sin adornos innecesarios y no demasiado complicado". Especialmente se podría argumentar que no debería haber demasiados casos específicos para cosas similares, etc. Por lo general, las matemáticas son bastante buenas para resumir las cosas en su esencia, y, sorprende, los matemáticos también han pasado algún tiempo pensando en la programación y las computadoras. .
Para la programación, existen 2 modelos abstractos bastante famosos:
Lo divertido es: si bien la máquina de Turing es bastante simple en su diseño, no es un sistema fácil de manejar y no creo que califique para "KISS inteligente". Pero el cálculo Lambda tiene más que ver con los lenguajes de programación que conocemos, y con las características pioneras de Lisp y Scheme del cálculo lambda, ha llegado a muchos lenguajes.
Lisp y Scheme son REALMENTE simples, al menos en cuanto a sintaxis. La sintaxis es un problema importante con los lenguajes de programación (por lo que probablemente se reinventan todo el tiempo). En el caso de C ++, es casi inmanejable para el cerebro humano predecir cómo el compilador interpreta algunas líneas de origen).
Lisps está reduciendo la complejidad sintáctica por completo mediante la introducción de una forma común para los comandos:
Esto puede ser una llamada a un método, como
así como una rama, bucle, etc.
(todo el código aquí es pseudo-Lisp / Dialect agnóstico)
La forma
también se puede interpretar como una lista
Y esa es la base de la simplicidad y belleza de Lisps. Debido a que las listas anidadas (como en el
if
ejemplo de la declaración) constituyen árboles y esos pueden ser fácilmente entendidos tanto por la máquina como por el humano frente a la máquina.Otra característica de Lisps son las macros . No entraré en detalles sucios aquí, pero dado que no hay una diferencia sintáctica entre las llamadas a funciones normales y "Sintaxis (huevo para bucles, declaración de variables, etc.), todo lo que el analizador tiene que manejar en otros lenguajes de programación) puede crear su propia sintaxis Las macros son en esencia Manipulaciones del árbol que constituye su programa.
Creo que Lisp tiene un BESO para programar. El hecho de que pueda manipular la sintaxis mediante el uso de macros ha llevado a un fenómeno que Lisp ha evolucionado de forma bastante dinámica. Necesita una nueva función de lenguaje como, digamos orientación de objeto, ¡simplemente escriba un sistema OOP con macros!
Si bien C se extendió en la rotonda 2 veces con las características de OOP (C ++, Obj. C) Lisp se extendió varias veces, al final hubo un ganador.
Esa es otra peculiaridad de Lisp, evoluciona (ver Clojure y Clojurescript para una nueva mutación interesante de lisp).
Por sus propiedades KISS, Lisps es favorecido como enseñanza de idiomas por algunos. Como Fogus describe en su plano de un lenguaje educativo ( http://blog.fogus.me/2013/01/21/enfield-a-programming-language-designed-for-pedagogy/ )
fuente