Solo tengo un conocimiento limitado de Lisp (tratando de aprender un poco en mi tiempo libre), pero hasta donde entiendo, las macros de Lisp permiten introducir nuevas construcciones y sintaxis del lenguaje describiéndolas en Lisp. Esto significa que se puede agregar una nueva construcción como una biblioteca, sin cambiar el compilador / intérprete Lisp.
Este enfoque es muy diferente al de otros lenguajes de programación. Por ejemplo, si quisiera extender Pascal con un nuevo tipo de bucle o algún idioma en particular, tendría que extender la sintaxis y la semántica del lenguaje y luego implementar esa nueva característica en el compilador.
¿Existen otros lenguajes de programación fuera de la familia Lisp (es decir, aparte de Common Lisp, Scheme, Clojure (?), Racket (?), Etc.) que ofrecen una posibilidad similar de extender el lenguaje dentro del lenguaje mismo?
EDITAR
Por favor, evite discusiones extensas y sea específico en sus respuestas. En lugar de una larga lista de lenguajes de programación que pueden ampliarse de una forma u otra, me gustaría entender desde un punto de vista conceptual qué es específico de las macros Lisp como mecanismo de extensión y qué lenguajes de programación que no son de Lisp ofrecen algún concepto. eso está cerca de ellos.
fuente
Respuestas:
Scala también lo hace posible (de hecho, fue diseñado conscientemente para admitir la definición de nuevas construcciones de lenguaje e incluso DSL completas).
Además de las funciones de orden superior, lambdas y curry, que son comunes en los lenguajes funcionales, hay algunas características especiales del lenguaje para habilitar esto *:
a.and(b)
es decir, es equivalente aa and b
en forma infijapara llamadas a funciones de un solo parámetro, puede usar llaves en lugar de llaves normales; esto (junto con el currículum) le permite escribir cosas como
donde
withPrintWriter
es un método simple con dos listas de parámetros, ambos contienen un solo parámetromyAssert(() => x > 3)
en una forma más corta comomyAssert(x > 3)
La creación de un ejemplo de DSL se trata en detalle en el Capítulo 11. Lenguajes específicos de dominio en Scala del libro gratuito Programming Scala .
* No quiero decir que sean exclusivos de Scala, pero al menos no parecen ser muy comunes. Sin embargo, no soy un experto en lenguajes funcionales.
fuente
Perl permite el preprocesamiento de su lenguaje. Si bien esto no se usa a menudo para cambiar radicalmente la sintaxis en el idioma, se puede ver en algunos de los ... módulos extraños:
También existe un módulo que permite que Perl ejecute código que parece haber sido escrito en Python.
Un enfoque más moderno para esto dentro de perl sería usar Filter :: Simple (uno de los módulos principales en perl5).
Tenga en cuenta que todos esos ejemplos involucran a Damian Conway, a quien se ha referido como el "Doctor loco de Perl". Todavía es una habilidad increíblemente poderosa dentro de Perl para torcer el idioma como uno lo quiere.
Existe más documentación para esta y otras alternativas en perlfilter .
fuente
Haskell
Haskell tiene "Template Haskell" y "Cuasiquotation":
http://www.haskell.org/haskellwiki/Template_Haskell
http://www.haskell.org/haskellwiki/Quasiquotation
Estas características permiten a los usuarios agregar dramáticamente a la sintaxis del lenguaje fuera de los medios normales. Estos también se resuelven en el momento de la compilación, lo que creo que es una gran necesidad (al menos para los lenguajes compilados) [1].
He usado la cuasiquotación en Haskell una vez antes para crear un emparejador de patrones avanzado sobre un lenguaje tipo C:
[1] De lo contrario, lo siguiente califica como extensión de sintaxis:
runFeature "some complicated grammar enclosed in a string to be evaluated at runtime"
que, por supuesto, es una carga de basura.fuente
forM
).Tcl tiene una larga historia de soporte de sintaxis extensible. Por ejemplo, aquí está la implementación de un ciclo que itera tres variables (hasta que se detiene) sobre los cardenales, sus cuadrados y sus cubos:
Eso se usaría así:
Este tipo de técnica se usa ampliamente en la programación de Tcl, y la clave para hacerlo de manera sensata son los comandos
upvar
yuplevel
(upvar
vincula una variable con nombre en otro ámbito a una variable local yuplevel
ejecuta un script en otro ámbito: en ambos casos, el1
indica que el alcance en cuestión es la persona que llama). También se usa mucho en el código que se combina con las bases de datos (ejecutando algo de código para cada fila en un conjunto de resultados), en Tk para GUI (para vincular devoluciones de llamada a eventos), etc.Sin embargo, eso es solo una fracción de lo que se hace. El lenguaje incrustado ni siquiera necesita ser Tcl; puede ser prácticamente cualquier cosa (siempre que equilibre sus llaves, las cosas se vuelven sintácticamente horribles si eso no es cierto, que es la gran mayoría de los programas) y Tcl puede simplemente enviarlo al idioma extranjero incorporado según sea necesario. Ejemplos de esto incluyen incrustar C para implementar comandos Tcl y el equivalente con Fortran. (Podría decirse que todos los comandos integrados de Tcl se realizan de esta manera en cierto sentido, ya que en realidad son solo una biblioteca estándar y no el lenguaje en sí).
fuente
Esto es en parte una cuestión de semántica. La idea básica de Lisp es que el programa son datos que pueden ser manipulados. Los lenguajes de uso común en la familia Lisp, como Scheme, realmente no le permiten agregar una nueva sintaxis en el sentido del analizador sintáctico; todo es solo listas entre paréntesis delimitadas por espacios. Es solo que, dado que la sintaxis central hace muy poco, puedes hacer casi cualquier construcción semántica con ella. Scala (discutido a continuación) es similar: las reglas de nombre de variable son tan liberales que puede hacer fácilmente DSL agradables (mientras se mantiene dentro de las mismas reglas de sintaxis central).
Estos lenguajes, si bien en realidad no le permiten definir una nueva sintaxis en el sentido de los filtros Perl, tienen un núcleo lo suficientemente flexible que puede usar para crear DSL y agregar construcciones de lenguaje.
La característica común importante es que le permiten definir construcciones de lenguaje que funcionan tan bien como las incorporadas, utilizando características expuestas por los idiomas. El grado de soporte para esta característica varía:
sin()
,round()
, etc., sin ninguna forma de implementar su propio.static_cast<target_type>(input)
,dynamic_cast<>()
,const_cast<>()
,reinterpret_cast<>()
) se pueden emular el uso de funciones de plantilla, que utiliza para Boostlexical_cast<>()
,polymorphic_cast<>()
,any_cast<>()
, ....for(;;){}
,while(){}
,if(){}else{}
,do{}while()
,synchronized(){}
,strictfp{}
) y no permitirá definir su propio. En cambio, Scala define una sintaxis abstracta que le permite invocar funciones utilizando una sintaxis similar a la estructura de control conveniente, y las bibliotecas la utilizan para definir efectivamente nuevas estructuras de control (por ejemplo,react{}
en la biblioteca de actores).Además, puede consultar la funcionalidad de sintaxis personalizada de Mathematica en el paquete de notación . (Técnicamente, pertenece a la familia Lisp, pero tiene algunas características de extensibilidad realizadas de manera diferente, así como la extensibilidad habitual de Lisp).
fuente
(defmacro ...)
. Actualmente estoy portando este lenguaje a Racket, solo por diversión. Pero, estoy de acuerdo en que es algo no muy útil, ya que la sintaxis de expresiones S es más que suficiente para la mayoría de las posibles semánticas útiles.(define-macro ...)
equivalente, que, a su vez, puede usar cualquier tipo de análisis interno.Rebol suena casi como lo que estás describiendo, pero un poco de lado.
En lugar de definir una sintaxis específica, todo en Rebol es una llamada de función: no hay palabras clave. (Sí, puede redefinir
if
ywhile
si realmente lo desea). Por ejemplo, esta es unaif
declaración:if
es una función que toma 2 argumentos: una condición y un bloque. Si la condición es verdadera, se evalúa el bloque. Suena como la mayoría de los idiomas, ¿verdad? Bueno, el bloque es una estructura de datos, no está restringido al código; este es un bloque de bloques, por ejemplo, y un ejemplo rápido de la flexibilidad del "código es datos":Siempre y cuando pueda apegarse a las reglas de sintaxis, extender este lenguaje, en su mayor parte, no será más que definir nuevas funciones. Algunos usuarios han estado respaldando características de Rebol 3 en Rebol 2, por ejemplo.
fuente
Ruby tiene una sintaxis bastante flexible, creo que es una forma de "extender el lenguaje dentro del lenguaje mismo".
Un ejemplo es el rastrillo . Está escrito en Ruby, es Ruby, pero parece hacer .
Para comprobar algunas posibilidades, puede buscar las palabras clave Ruby y metaprogramación .
fuente
Ampliar la sintaxis de la forma en que está hablando le permite crear idiomas específicos de dominio. Entonces, quizás la forma más útil de reformular su pregunta es, ¿qué otros idiomas tienen un buen soporte para idiomas específicos de dominio?
Ruby tiene una sintaxis muy flexible, y muchas DSL han florecido allí, como el rastrillo. Groovy incluye mucha de esa bondad. También incluye transformaciones AST, que son más directamente análogas a las macros Lisp.
R, el lenguaje para la computación estadística, permite que las funciones no evalúen sus argumentos. Utiliza esto para crear un DSL para especificar la fórmula de regresión. Por ejemplo:
significa "ajustar una línea de la forma k0 + k1 * a + k2 * b a los valores en y".
significa "ajustar una línea de la forma k0 + k1 * a + k2 * b + k3 * a * b a los valores en y".
Y así.
fuente
Converge es otro lenguaje de metaprogramación no lispy. Y, hasta cierto punto, C ++ también califica.
Podría decirse que MetaOCaml está bastante lejos de Lisp. Para un estilo totalmente diferente de extensibilidad de sintaxis, pero aún bastante potente, eche un vistazo a CamlP4 .
Nemerle es otro lenguaje extensible con una metaprogramación de estilo Lisp, aunque está más cerca de lenguajes como Scala.
Y, Scala pronto se convertirá en un lenguaje así también.
Editar: Olvidé el ejemplo más interesante: JetBrains MPS . No solo está muy distante de nada de Lispish, es incluso un sistema de programación no textual, con un editor que opera directamente en un nivel AST.
Edit2: para responder una pregunta actualizada: no hay nada único y excepcional en las macros de Lisp. En teoría, cualquier lenguaje puede proporcionar dicho mecanismo (incluso lo hice con C simple). Todo lo que necesita es un acceso a su AST y la capacidad de ejecutar código en tiempo de compilación. Alguna reflexión podría ayudar (consultar sobre los tipos, las definiciones existentes, etc.).
fuente
Prolog permite definir nuevos operadores que se traducen a términos compuestos del mismo nombre. Por ejemplo, esto define un
has_cat
operador y lo define como un predicado para verificar si una lista contiene el átomocat
:El
xf
medio quehas_cat
es un operador postfix; el usofx
lo convertiría en un operador de prefijo yxfx
lo convertiría en un operador de infijo, tomando dos argumentos. Consulte este enlace para obtener más detalles sobre cómo definir operadores en Prolog.fuente
TeX no se encuentra en la lista. Todos ustedes lo saben, ¿verdad? Se ve algo como esto:
... excepto que puede redefinir la sintaxis sin restricciones. A cada token (!) En el idioma se le puede asignar un nuevo significado. ConTeXt es un paquete de macros que ha reemplazado las llaves con llaves cuadradas:
El paquete de macros más común LaTeX también redefine el lenguaje para sus propósitos, por ejemplo, agregando la
\begin{environment}…\end{environment}
sintaxis.Pero no se detiene ahí. Técnicamente, también podría redefinir los tokens para analizar lo siguiente:
Si, absolutamente posible. Algunos paquetes usan esto para definir pequeños lenguajes específicos de dominio. Por ejemplo, el paquete TikZ define una sintaxis concisa para dibujos técnicos, que permite lo siguiente:
Además, TeX es Turing completo, por lo que literalmente puede hacer todo con él. Nunca he visto esto utilizado en todo su potencial porque sería bastante inútil y muy complicado, pero es completamente posible hacer que el siguiente código sea analizable simplemente redefiniendo tokens (pero esto probablemente iría a los límites físicos del analizador, debido a que forma en que está construido):
fuente
Boo te permite personalizar mucho el idioma en tiempo de compilación a través de macros sintácticas.
Boo tiene una "tubería compilador extensible". Eso significa que el compilador puede llamar a su código para realizar transformaciones AST en cualquier momento durante la canalización del compilador. Como saben, cosas como Java's Generics o C # 's Linq son solo transformaciones de sintaxis en tiempo de compilación, por lo que esto es bastante poderoso.
En comparación con Lisp, la principal ventaja es que funciona con cualquier tipo de sintaxis. Boo está utilizando una sintaxis inspirada en Python, pero probablemente podría escribir un compilador extensible con una sintaxis C o Pascal. Y dado que la macro se evalúa en tiempo de compilación, no hay penalización de rendimiento.
Los inconvenientes, en comparación con Lisp, son:
Por ejemplo, así es como podría implementar una nueva estructura de control:
uso:
que luego se traduce, en tiempo de compilación, a algo como:
(Desafortunadamente, la documentación en línea de Boo siempre está desactualizada y ni siquiera cubre cosas avanzadas como esta. La mejor documentación para el idioma que conozco es este libro: http://www.manning.com/rahien/ )
fuente
La evaluación de Mathematica se basa en la coincidencia y reemplazo de patrones. Eso le permite crear sus propias estructuras de control, cambiar las estructuras de control existentes o cambiar la forma en que se evalúan las expresiones. Por ejemplo, podría implementar una "lógica difusa" como esta (un poco simplificado):
Esto anula la evaluación de los operadores lógicos predefinidos &&, || ,! y la
If
cláusula incorporada .Puede leer estas definiciones como definiciones de funciones, pero el significado real es: si una expresión coincide con el patrón descrito en el lado izquierdo, se reemplaza con la expresión en el lado derecho. Podría definir su propia cláusula If de esta manera:
SetAttributes[..., HoldRest]
le dice al evaluador que debe evaluar el primer argumento antes de la coincidencia del patrón, pero mantener la evaluación para el resto hasta después de que el patrón haya sido emparejado y reemplazado.Esto se usa ampliamente dentro de las bibliotecas estándar de Mathematica para, por ejemplo, definir una función
D
que tome una expresión y evalúe su derivada simbólica.fuente
Metalua es un lenguaje y un compilador compatible con Lua que proporciona esto.
Diferencias con Lisp:
Un ejemplo de aplicación es la implementación de la coincidencia de patrones tipo ML.
Ver también: http://lua-users.org/wiki/MetaLua
fuente
Si está buscando idiomas que sean extensibles, debería echar un vistazo a Smalltalk.
En Smalltalk, la única forma de programar es extender el lenguaje. No hay diferencia entre el IDE, las bibliotecas o el lenguaje en sí. Todos están tan entrelazados que Smalltalk a menudo se conoce como un entorno en lugar de un lenguaje.
No escribe aplicaciones independientes en Smalltalk, sino que extiende el entorno del lenguaje.
Visite http://www.world.st/ para ver un puñado de recursos e información.
Me gustaría recomendar Pharo como dialecto de entrada al mundo de Smalltalk: http://pharo-project.org
Espero que haya ayudado!
fuente
Existen herramientas que permiten crear idiomas personalizados sin escribir un compilador completo desde cero. Por ejemplo, está Spoofax , que es una herramienta de transformación de código: coloca reglas gramaticales y de transformación de entrada (escritas de manera declarativa de muy alto nivel), y luego puede generar código fuente Java (u otro lenguaje, si le importa lo suficiente) de un lenguaje personalizado diseñado por ti.
Por lo tanto, sería posible tomar la gramática del lenguaje X, definir la gramática del lenguaje X '(X con sus extensiones personalizadas) y la transformación X' → X, y Spoofax generará un compilador X '→ X.
Actualmente, si entiendo correctamente, el mejor soporte es para Java, con el desarrollo de C # en desarrollo (o eso escuché). Sin embargo, esta técnica podría aplicarse a cualquier lenguaje con gramática estática (por ejemplo, probablemente no Perl ).
fuente
Forth es otro idioma que es altamente extensible. Muchas implementaciones de Forth consisten en un pequeño núcleo escrito en ensamblador o C, luego el resto del lenguaje está escrito en el mismo Forth.
También hay varios lenguajes basados en pila que están inspirados en Forth y comparten esta característica, como Factor .
fuente
Funge-98
La función de huella digital de Funge-98 permite hacer una reestructuración completa de toda la sintaxis y la semántica del lenguaje. Pero solo si el implementador proporciona un mecanismo de huellas dactilares que permitió al usuario alterar programáticamente el idioma (esto es teóricamente posible implementar dentro de la sintaxis y semántica Funge-98 normales). Si es así, uno podría literalmente hacer que el resto del archivo (o cualquier parte del archivo) actúe como C ++ o Lisp (o lo que quiera).
http://quadium.net/funge/spec98.html#Fingerprints
fuente
Para obtener lo que estás buscando, realmente necesitas esos paréntesis y una falta de sintaxis. Algunos lenguajes basados en sintaxis pueden acercarse, pero no es lo mismo que una macro real.
fuente