C # Dev - He intentado Lisps, pero no lo consigo [cerrado]

37

Después de unos meses de aprender y jugar con Lisp, tanto CL como un poco de Clojure, todavía no veo una razón convincente para escribir nada en lugar de C #.

Realmente me gustaría algunas razones convincentes, o para que alguien señale que me estoy perdiendo algo realmente grande .

Los puntos fuertes de Lisp (según mi investigación):

  • Notación compacta y expresiva: más que C #, sí ... pero parece que también puedo expresar esas ideas en C #.
  • Soporte implícito para programación funcional - C # con métodos de extensión LINQ:
    • mapcar = .Seleccionar (lambda)
    • mapcan = .Select (lambda) .Agregre ((a, b) => a.Union (b))
    • coche / primero = .Primero ()
    • cdr / rest = .Skip (1) .... etc.
  • Compatibilidad con funciones Lambda y de orden superior: C # tiene esto y la sintaxis es posiblemente más simple:
    • "(lambda (x) (cuerpo))" versus "x => (cuerpo)"
    • "# (" con "%", "% 1", "% 2" es bueno en Clojure
  • Despacho de método separado de los objetos: C # tiene esto a través de métodos de extensión
  • Despacho multimetodo: C # no tiene esto de forma nativa, pero podría implementarlo como una llamada de función en unas pocas horas
  • El código es datos (y macros): tal vez no he "obtenido" macros, pero no he visto un solo ejemplo en el que la idea de una macro no se pueda implementar como una función; no cambia el "idioma", pero no estoy seguro de que sea una fortaleza
  • DSL: solo puede hacerlo a través de la composición de funciones ... pero funciona
  • Programación "exploratoria" sin tipo: para estructuras / clases, las autopropertías de C # y el "objeto" funcionan bastante bien, y puede escalar fácilmente a una escritura más fuerte a medida que avanza
  • Se ejecuta en hardware que no es de Windows. Fuera de la universidad, solo he conocido a una persona que no ejecuta Windows en casa, o al menos una VM de Windows en * nix / Mac. (Por otra parte, tal vez esto es más importante de lo que pensaba y me acaban de lavar el cerebro ...)
  • El REPL para el diseño de abajo hacia arriba: Ok, admito que esto es realmente muy agradable, y lo extraño en C #.

Cosas que me faltan en Lisp (debido a una combinación de C #, .NET, Visual Studio, Resharper):

  • Espacios de nombres. Incluso con métodos estáticos, me gusta vincularlos a una "clase" para clasificar su contexto (Clojure parece tener esto, CL no parece tener).
  • Gran compilación y soporte en tiempo de diseño
    • el sistema de tipos me permite determinar la "corrección" de las estructuras de datos que paso
    • todo lo que está mal escrito está subrayado en tiempo real; No tengo que esperar hasta el tiempo de ejecución para saber
    • las mejoras de código (como el uso de un enfoque FP en lugar de uno imperativo) se sugieren automáticamente
  • Herramientas de desarrollo de GUI: WinForms y WPF (sé que Clojure tiene acceso a las bibliotecas de GUI de Java, pero son completamente ajenas a mí).
  • Herramientas de depuración de la interfaz gráfica de usuario: puntos de interrupción, paso, paso, inspectores de valor (texto, xml, personalizado), relojes, depuración por subproceso, puntos de interrupción condicionales, ventana de pila de llamadas con la capacidad de saltar al código en cualquier nivel en la pila
    • (Para ser justos, mi período con Emacs + Slime parecía proporcionar algo de esto, pero soy parcial con el enfoque de VS GUI)

Realmente me gusta el bombo que rodea a Lisp y le di una oportunidad.

Pero, ¿hay algo que pueda hacer en Lisp que no pueda hacer tan bien en C #? Puede ser un poco más detallado en C #, pero también tengo autocompletar.

¿Qué me estoy perdiendo? ¿Por qué debería usar Clojure / CL?

talonx
fuente
44
Como ya está programando en un estilo funcional y parece que confía bastante en la infraestructura de Windows, no veo ninguna razón convincente para que cambie de C #. La mayoría de las personas que conozco usan Mac o Linux, por lo que depende mucho de la multitud con la que se ejecuta (he usado todas las versiones de Windows desde 3.1, pero todavía prefiero trabajar con software multiplataforma y sistemas * nix en general ... pero luego no hago ningún trabajo de GUI nativo, todos los servidores y cosas de la interfaz de usuario web)
Sean Corfield
2
Podrías echar un vistazo a The Swine Before Perl . Es una charla divertida para escuchar y presenta un ejemplo simple de para qué sirven las macros.
Matthias Benkard
44
"No he visto un solo ejemplo en el que la idea de una macro no se pueda implementar como una función". Me gustaría ver cómo escribiría ANDo ORcomo una función. Podría hacerse (dado LAMBDA, que también es una macro), pero no veo una forma obvia de hacerlo que no apestara totalmente.
1
"Espacios de nombres ... Clojure parece tener esto, CL no parece". ¿Puede ser más específico acerca de lo que está buscando? Realmente no he usado Clojure, pero sus espacios de nombres se parecen mucho a los paquetes de CL.
44
Jonathan: CL usa :(o ::para símbolos no exportados) para nombrar símbolos en paquetes, por ejemplo, su ejemplo aquí usaría http:sessiony chat:session.

Respuestas:

23

Las macros le permiten escribir compiladores sin dejar la comodidad de su idioma. Las DSL en lenguajes como C # generalmente equivalen a un intérprete de tiempo de ejecución. Dependiendo de su dominio, eso podría ser problemático por razones de rendimiento. La velocidad sí importa , de lo contrario estaría codificando en un lenguaje interpretado y no en C #.

Además, las macros también le permiten considerar factores humanos: ¿cuál es la sintaxis más fácil de usar para un DSL en particular? Con C # no hay mucho que pueda hacer sobre la sintaxis.

Entonces Lisp le permite participar en el diseño del lenguaje sin sacrificar un camino hacia la eficiencia . Y aunque estos problemas pueden no ser importantes para usted , son extremadamente importantes ya que subyacen en los cimientos de todos los lenguajes de programación útiles orientados a la producción. En C #, este elemento fundamental está expuesto a usted en una forma muy limitada en el mejor de los casos.

La importancia de las macros no se pierde en otros idiomas: me viene a la mente la plantilla Haskell, camlp4. Pero, una vez más, es una cuestión de usabilidad : las macros Lisp hasta la fecha son probablemente la implementación más fácil de usar pero poderosa de la transformación en tiempo de compilación.

Entonces, en resumen, lo que la gente generalmente hace con lenguajes como C # es construir DSIL (lenguajes interpretados específicos del dominio). Lisp te da la oportunidad de construir algo mucho más radical, DSP (Paradigmas específicos de dominio). Racket (anteriormente PLT-Scheme) es particularmente inspirador a este respecto: tienen un lenguaje perezoso (Lazy Racket), uno escrito (Typed Racket), Prolog y Datalog, todos integrados idiomáticamente a través de macros. Todos estos paradigmas proporcionan soluciones poderosas para grandes clases de problemas, problemas que no se resuelven mejor mediante programación imperativa o incluso paradigmas de FP.

Robert Harvey
fuente
3
Lo que encuentro interesante sobre ese argumento es que nunca me he encontrado con una oportunidad (o simplemente la he pasado por alto en mi ignorancia) para implementar un DSL / DSIL. ¿Podría explicar el poder de las DSL? (Tanto para un desarrollador como para un usuario). Esto suena como si fuera la gran cosa que realmente me estoy perdiendo.
15
@Jonathan Mitchem, ya está utilizando muchos DSL externos: archivos de configuración, scripts de compilación, configuraciones de ORM, generadores de código visual (por ejemplo, editor de winforms), XAML, generadores de analizadores, etc. Los DSL integrados son aún mejores, ya que no lo hace. No necesita una herramienta de compilación separada para ellos, y se sirven en un solo idioma. Y debe estar familiarizado con al menos dos de los DSL integrados: expresiones regulares y SQL.
SK-logic
Wow, esta bién. Nunca pensé en ellos como DSL. Ahora, me he alejado intencionalmente de mucho de eso, sobre la base de "cualquier cosa que pueda hacer con C #, me quedaré con C # para". Me gusta seguir con una herramienta con un comportamiento constante, personalmente. Pero sí entiendo el valor de las DSL en estos ejemplos.
2
@ Jonathan Mitchem, el problema con cualquier herramienta dada es que no necesariamente será adecuado para un propósito determinado. Tienes que elegir diferentes herramientas para diferentes tareas. Y el poder de cualquier metalenguaje, incluido Lisp, es que no tiene que cambiar completamente a otra herramienta, ya que puede hacer crecer sus propias herramientas específicas de dominio además de su idioma principal. Sugiero que eche un vistazo a Nemerle, sería más fácil entenderlo para un desarrollador de C #, y sus macros son casi tan poderosas como las de Lisp.
SK-logic
3
"Me gusta seguir con una herramienta ..." Las DSL diseñadas correctamente en Lisp nunca ocultan todo el poder de Lisp, por lo que aún tienes una herramienta. Simplemente agregan al vocabulario de una manera que hace que la codificación de un dominio particular sea más "natural", es decir, con mejores abstracciones y una organización general.
15

Afortunadamente, casi todo lo que originalmente estaba disponible solo en Lisps ha migrado a muchos otros idiomas modernos. La mayor excepción es la filosofía "código es datos" y las macros.

El código es datos (y macros): tal vez no he "obtenido" macros, pero no he visto un solo ejemplo en el que la idea de una macro no se pueda implementar como una función

Sí, las macros son difíciles de asimilar. La metaprogramación en general es difícil de asimilar. Si vio alguna macro que podría implementarse como una función, entonces el programador lo estaba haciendo mal. Las macros solo deben usarse cuando una función no funciona.

El ejemplo de "hola mundo" de una macro es escribir "if" en términos de cond. Si está realmente interesado en aprender el poder de las macros, intente definir un "my-if" en clojure, utilizando funciones y observe cómo se rompe. No quiero ser misterioso, nunca he visto a nadie comenzar a entender las macros solo por explicación, pero esta presentación de diapositivas es una muy buena introducción: http://www.slideshare.net/pcalcado/lisp-macros-in -20 minutos-presentando-clojure-presentación

He tenido pequeños proyectos en los que he guardado literalmente cientos / miles de líneas de código usando macros (en comparación con lo que hubiera tomado en Java). Gran parte de Clojure se implementa en Clojure, que solo es posible debido al sistema macro.

mblinn
fuente
3
Sin tocar ningún código, me encontré con el escenario de implementar un "si" sin macros en mi cabeza, tanto en Clojure como en C #. Efectivamente (o literalmente) no se puede hacer. Admito que está bien. Pero me falta la conexión de cómo eso es útil para mí. ¿Qué puedo hacer con las macros (además de escribir "si") que no puedo hacer con un diseño inteligente o OO inteligente? "¿Cómo podría ayudarme?" No es un criterio válido para evaluar un idioma, pero sí para evaluar si lo usaré. (Realmente quiero que valga la pena cambiar a Lisp, pero no ha llegado al punto de convertirse en una alternativa viable).
1
Me tomará un poco de tiempo entender ese ejemplo. Para mí, "guardar código repetitivo" generalmente invoca el término "refactorización", que es parte de por qué las explicaciones no me han convencido. Tengo una solución que funciona en cada instancia que la he aplicado. Creo que la pregunta es cómo identifica los casos en los que puede usar una macro.
1
Estoy de acuerdo en que las macros valen la pena, pero no es cierto que my-if las requiera, puede escribirse como una función de orden superior (vea un ejemplo de CL a continuación). Realmente solo necesita macros si desea recorrer el código o colocarlo en un nuevo contexto léxico. (defun my-if (test then &optional else) (cond (test (funcall then)) ('otherwise (funcall else))))
1
Básicamente, con una función, tiene la opción de aceptar el código entre comillas, que puede caminar pero le falta su contexto léxico porque está caminando y ejecutándolo en el contexto léxico de su función. O puede aceptar cierres, que mantienen su contexto léxico, pero solo puede llamar, no caminarlos o agregar algo a su contexto léxico. Para hacer ambas cosas, necesita una macro, que puede recorrer y ajustar el código antes de que su expansión se coloque en el contexto léxico de la llamada a la macro.
77
@Jonathan Mitchem, la sintaxis LINQ es un buen ejemplo de lo que se puede hacer con macros, pero tuvo que implementarse en un núcleo de lenguaje, ya que el lenguaje no admite ninguna metaprogramación decente.
SK-logic
14

No soy un experto en Common Lisp, pero sé que tanto C # como Clojure son bastante buenos, así que espero que esta sea una perspectiva útil.

  • Sintaxis simple => potencia - Lisps trata sobre la sintaxis más simple que podría funcionar. Las expresiones S son todo lo que necesitas. Una vez que has asimilado "(función-llamada arg1 arg2 arg3)", entonces básicamente lo tienes. El resto es solo elegir las llamadas a funciones y argumentos correctos. Parece casi trivialmente simple, pero el hecho es que esta simplicidad es lo que le da a Lisps tanta fuerza y ​​flexibilidad, y en particular habilita las capacidades de metaprogramación por las que Lisp es famoso. por ejemplo, si quiero que una macro invoque varias funciones diferentes con los mismos argumentos, simplemente hago algo como lo siguiente (no se trata solo de una aplicación de función de tiempo de ejecución; es una macro que genera código compilado como si hubiera escrito todas las llamadas a funciones individuales a mano):
(defmacro print-many [functions args]  
  `(do   
     ~@(map   
        (fn [function] `(println (~function ~@args)))   
        functions)))

(print-many [+ - * /] [10 7 2])  
19  
1  
140  
5/7
  • Como resultado de la sintaxis increíblemente simple, la filosofía de "código es datos" de Lisp es mucho más poderosa que cualquier cosa que pueda hacer con la composición / plantillas / genéricos de funciones, etc. Es más que solo DSL, es generación de código y manipulación, en tiempo de compilación , como característica fundamental del lenguaje. Si intentas obtener este tipo de capacidades en un lenguaje no homoicónico como C # o Java, eventualmente terminarás reinventando a Lisp. De ahí la famosa Décima Regla de Greenspuns: "Cualquier programa C o Fortran suficientemente complicado contiene una implementación ad hoc, especificada informalmente, llena de errores y lenta de la mitad de Common Lisp". Si alguna vez te has encontrado inventando un lenguaje de plantillas / control de flujo completo dentro de tu aplicación, entonces probablemente sabes lo que quiero decir ...

  • La concurrencia es una fortaleza única de Clojure (incluso en relación con otros Lisps), pero si cree que el futuro de la programación significará tener que escribir aplicaciones que escalen a máquinas altamente multinúcleo, entonces realmente debería considerar esto: es bastante innovador Clojure STM (Software Transactional Memory) funciona porque Clojure adopta construcciones de inmutabilidad y concurrencia sin bloqueo basadas en una novedosa separación de identidad y estado (ver el excelente video de Rich Hickey sobre identidad y estado ). Sería muy doloroso injertar este tipo de capacidad de concurrencia en un entorno de lenguaje / tiempo de ejecución donde una proporción significativa de las API funciona en objetos mutables.

  • Programación funcional : Lisps enfatiza la programación con funciones de orden superior. Tiene razón en que puede emularse en objetos OO con objetos de cierre / función, etc. pero la diferencia es que todo el lenguaje y la biblioteca estándar están diseñados para ayudarlo a escribir código de esta manera. Por ejemplo, las secuencias de Clojure son perezosas , por lo que pueden ser infinitamente largas a diferencia de sus ArrayLists o HashMaps en C # / Java. Esto hace que algunos algoritmos sean mucho más fáciles de expresar, por ejemplo, lo siguiente implementa una lista infinita de números de Fibonacci:

    (def fibs (lazy-cat '(0 1) (map + fibs (drop 1 fibs))))

  • Escritura dinámica : los Lisps tienden a estar en el extremo "dinámico" del espectro del lenguaje de programación funcional (a diferencia de los idiomas como Haskell con una concepción muy fuerte y hermosa de los tipos estáticos). Esto tiene ventajas y desventajas, pero diría que los lenguajes dinámicos tienden a favorecer la productividad de la programación debido a una mayor flexibilidad. Esta escritura dinámica tiene un costo de rendimiento de tiempo de ejecución menor, pero si eso le molesta, siempre puede agregar sugerencias de tipo a su código para obtener una escritura estática. Básicamente: obtienes comodidad de forma predeterminada y rendimiento si estás dispuesto a hacer un poco de trabajo extra para conseguirlo.

  • Desarrollo interactivo : en realidad creo que puede obtener algún tipo de REPL para C # 4.0, pero en Lisp es una práctica estándar en lugar de una novedad. No tiene que hacer un ciclo de compilación / compilación / prueba en absoluto: escribe su código directamente en el entorno de ejecución y solo más tarde lo copia en sus archivos de origen. Te enseña una forma muy diferente de codificar, donde ves resultados inmediatamente y refinas tu solución de forma iterativa.

Algunos comentarios rápidos sobre las cosas que ves como "faltantes":

  • Como dices, Clojure tiene espacios de nombres. De hecho, son espacios de nombres dinámicos: puede actualizarlos en tiempo de ejecución; esto proporciona algunos beneficios sorprendentes para el desarrollo interactivo. Me divertí mucho redefiniendo funciones bajo la nariz de un hilo en ejecución, o pirateando una estructura de datos en vivo ...
  • Soporte de tiempo de compilación / diseño - no es realmente relevante si está codificando en un REPL - simplemente lo hace y ve el resultado directamente. Dicho esto, Clojure está comenzando a obtener un soporte IDE mejorado, por ejemplo, el complemento CounterClockwise para Eclipse .
  • Desarrollo de GUI: sí, tendrá que aprender una nueva API, pero ese siempre será el caso al cambiar de idioma ... la buena noticia es que las API de Java no son tan difíciles y ahora hay algunos Clojure interesantes envoltorios / ejemplos específicos que parecen bastante fáciles de usar. Vea esta pregunta sobre el desarrollo de Clojure GUI para algunos buenos ejemplos
  • Depuración de la GUI: esto funciona con el depurador de la GUI en Eclipse, suponiendo que use CounterClockwise o Enclojure si usa Netbeans. Dicho esto, no uso esta capacidad: encuentro que la depuración interactiva en REPL es más productiva.

Personalmente, encuentro las ventajas de Clojure / Lisp bastante convincentes, y no planeo volver a C # / Java (¡más allá de lo que necesito para tomar prestadas todas las bibliotecas útiles!).

Sin embargo, me llevó unos meses entenderlo todo. Si está realmente interesado en aprender Lisp / Clojure como una experiencia de aprendizaje esclarecedora, no hay sustituto para sumergirse y obligarse a implementar algunas cosas .....

mikera
fuente
1
Gracias por la gran enumeración de ideas. De hecho, uso muchas secuencias perezosas a través de la construcción IEnumerable <> en C # (y no he tocado una ArrayList desde 2005 más o menos), pero entiendo la idea. Las primitivas de concurrencia de Clojure son extremadamente impresionantes. Hace unos años estaba haciendo computación grid en C #, concurrencia en muchas máquinas multinúcleo, y todavía creo que ese es el futuro. Probablemente la idea más grande que he recibido de todos es "realmente hay que cavar y experimentarlo, realmente no se puede explicar". Entonces, voy a seguir cavando y seguir experimentando.
1
Genial, me alegro de haber ayudado, ¡y ese es absolutamente el espíritu correcto!
mikera
10

C # tiene muchas características, pero la mezcla se siente diferente. Que algún idioma tenga una característica aún no significa que sea fácil de usar, paradigmático y esté bien integrado con el resto del lenguaje.

Common Lisp tiene esta mezcla:

  • Expresiones s como una sintaxis de datos
  • código como datos conduce a macros y otras técnicas de computación simbólica
  • El lenguaje dinámico permite modificaciones en tiempo de ejecución (enlace tardío, MOP, ...)
  • fuertemente tipado, tipado dinámicamente
  • uso interactivo con compilador o intérprete incremental
  • Evaluador como motor de ejecución central
  • memoria administrada con recolección de basura
  • Lenguaje híbrido con uso intensivo de programación imperativa, funcional y orientada a objetos. Se pueden agregar otros paradigmas (reglas, lógica, restricciones, ...).

Ahora, otros lenguajes, como C #, también tienen eso. Pero a menudo no con el mismo énfasis.

C # tiene

  • Sintaxis tipo C
  • orientado principalmente a objetos imperativos, con algunas características de FP
  • mecanografiado estáticamente
  • principalmente uso por lotes con un compilador por lotes
  • memoria administrada con recolección de basura

No ayuda que C # tenga, por ejemplo, características de manipulación de código, si no se usan ampliamente como en Lisp. En Lisp no encontrará muchos programas que no hagan un uso intensivo de las macros en todo tipo de formas simples y complejas. El uso de la manipulación de código es realmente una gran característica en Lisp. Emular algunos usos es posible en muchos idiomas, pero no lo convierte en una característica central como en Lisp. Vea libros como 'On Lisp' o 'Practical Common Lisp' para ver ejemplos.

Lo mismo para el desarrollo interactivo. Si desarrolla un sistema CAD grande, la mayor parte del desarrollo será interactivo desde el editor y REPL, directamente en la aplicación CAD en ejecución. Puede emular mucho de eso en C # u otros lenguajes, a menudo implementando un lenguaje de extensión. Para Lisp sería estúpido reiniciar la aplicación CAD en desarrollo para agregar cambios. El sistema CAD también contendría un Lisp mejorado que habría agregado características de lenguaje (en forma de macros y descripciones simbólicas) para describir objetos CAD y sus relaciones (parte de, contenido, ...). Se pueden hacer cosas similares en C #, pero en Lisp este es el estilo de desarrollo predeterminado.

Si desarrolla un software complejo utilizando un proceso de desarrollo interactivo o si su software tiene una parte simbólica sustancial (meta programación, otros paradigmas, álgebra informática, composición musical, planificación, ...), entonces Lisp puede ser para usted.

Si trabaja en el entorno de Windows (no lo hago), entonces C # tiene una ventaja, ya que es un lenguaje de desarrollo principal de Microsoft. Microsoft no admite ninguna forma de Lisp.

Usted escribe:

  • Espacios de nombres. Incluso con métodos estáticos, me gusta vincularlos a una "clase" para clasificar su contexto (Clojure parece tener esto, CL no parece tener).

Common Lisp no adjunta espacios de nombres a las clases. Los espacios de nombres son proporcionados por paquetes de símbolos y son independientes de las clases. Necesita reorientar su enfoque a la programación orientada a objetos si usa CLOS.

  • Gran compilación y soporte en tiempo de diseño, el sistema de tipos me permite determinar la "corrección" de las estructuras de datos que paso, todo lo que está mal escrito está subrayado en tiempo real; No tengo que esperar hasta el tiempo de ejecución para saber que las mejoras de código (como el uso de un enfoque FP en lugar de uno imperativo) se sugieren automáticamente

Usa el compilador Lisp. Le informa acerca de variables desconocidas, puede tener recomendaciones de estilo (dependiendo del compilador utilizado), le da pistas de eficiencia, ... El compilador está a una pulsación de tecla.

  • Herramientas de desarrollo de GUI: WinForms y WPF (sé que Clojure tiene acceso a las bibliotecas de GUI de Java, pero son completamente ajenas a mí).

Los Lisps comerciales como LispWorks y Allegro CL ofrecen cosas similares en Windows.

  • Herramientas de depuración de la GUI: puntos de interrupción, paso a paso, paso a paso, inspectores de valor (texto, xml, personalizado), relojes, depuración por subproceso, puntos de interrupción condicionales, ventana de pila de llamadas con la capacidad de saltar al código en cualquier nivel en la pila (para ser justos, mi paso con Emacs + Slime parecía proporcionar algo de esto, pero soy parcial al enfoque impulsado por VS GUI)

Los Lisps comerciales como LispWorks y Allegro CL ofrecen todo eso bajo Windows.

Rainer Joswig
fuente
1
Mi crítica no es realmente de Lisps. Me parece que son bastante capaces. Estoy buscando qué puede hacer / darme Lisp que aún no hago. Entiendo completamente que la mayoría de los desarrolladores de C # no usan muchas de las "nuevas" características del lenguaje. Creo que el punto más importante es que lo hago, y sinceramente, fuera de la GUI, casi escribo en un estilo 100% FP. FP fue un salto conceptual, pero valió la pena. Sin embargo, cambiar a un Lisp desde donde estoy parece proporcionar pocos beneficios. Espero encontrar que hay una razón por la que debería seguir dándole una oportunidad.
@Jonathan Mitchem: C # realmente no se parece a un lenguaje FP, incluso si el lenguaje tiene algunas funciones que lo admiten y algunas bibliotecas podrían usarlas. Si desea hacer la programación de FP en el ecosistema de Microsoft, F # podría ser para usted.
@Rainer No, no parece un lenguaje FP, pero puede hacerlo, y de manera bastante compacta. Y cuando quiero / necesito código imperativo, u OO, también puedo hacerlo. (ligeramente fuera de tema: realmente no considero que F # sea un lenguaje serio en el que valga la pena profundizar, prefiero entender las mónadas en Haskell. Pero cuando era un desarrollador de C ++ y salió C #, no lo consideré un lenguaje "real" [y en aquel entonces no lo era, en mi humilde opinión])
@ Jonathan Mitchem: ¿por qué debería mirar? Escribí que C # admite algunas características de FP. Simplemente se agregó a un lenguaje orientado a objetos imperativo y no lo hace idiomático, como en, por ejemplo, Lisp, Scheme y especialmente no se compara con SML, F #, Haskell u otros lenguajes principalmente funcionales. Mi punto es: algo de FP es posible en C #, pero no es una característica central.
5

Rainiero Joswig lo dijo mejor.

Para mí, el poder de Lisp no se puede entender simplemente mirando una lista de características de Lisp. Para Lisp, y Common Lisp en particular, el todo es mayor que la suma de las partes. No es solo REPL más expresiones s más macros más despacho de métodos múltiples más cierres más paquetes más CLOS más reinicios más X, Y y Z. Es todo el shebang todo ingeniosamente integrado. Es la experiencia cotidiana que es muy diferente a la experiencia cotidiana de otros lenguajes de programación.

Lisp se ve diferente, se usa de manera diferente, uno se acerca a la programación de manera diferente cuando lo usa. Es elegante, completo, grande y sin costuras. No está exento de imperfecciones y pecadillos. Ciertamente, hay comparaciones con otros idiomas que podrían hacerse donde podría no salir adelante. Pero de ninguna manera Lisp es solo lenguaje X más REPL y macros, o lenguaje Y más despacho y reinicio de métodos múltiples.


fuente
3
El código es datos (y macros): tal vez no he "obtenido" macros, pero no he visto un solo ejemplo en el que la idea de una macro no se pueda implementar como una función; no cambia el "idioma", pero no estoy seguro de que sea una fortaleza

Típicamente, una macro debe usarse para algo que no se puede expresar como una llamada a función. No sé si hay un condicional de tipo switch en C # (similar a lo que existe en C, como switch (form) {case ...}), pero supongamos que sí y tiene casi las mismas restricciones ( que requiere un tipo integral).

Ahora, supongamos que le gustaría algo que se parezca a eso, pero que se despache en cadenas. En lisp (bueno, específicamente en Common Lisp y probablemente en otros), eso es "solo" una macro de distancia y si restringe al usuario a tener cadenas constantes (probablemente no arduas) para comparar, puede calcular (tiempo de compilación) una secuencia mínima de pruebas para determinar qué caso coincide (o usar una tabla hash, almacenando cierres, tal vez).

Si bien es posible expresar eso como una función (cadena de comparación, una lista de casos y cierres para ejecutar), probablemente no se verá como "código normal" y es ahí donde las macros lisp tienen una victoria definitiva. Probablemente ha utilizado un buen número de macros o formas especiales taht son macro-como, como defun, defmacro, setf, cond, loopy muchos más.

Vatine
fuente
2

Este es el mejor artículo explicativo que he encontrado que saca al lector de la superficie de la defensa de Lisp para mostrar algunas de las implicaciones más profundas de los conceptos que se utilizan:

http://www.defmacro.org/ramblings/lisp.html

Recuerdo haber leído en otra parte una idea como esta: otros idiomas han adoptado varias características de Lisp; recolección de basura, tipeo dinámico, funciones anónimas, cierres para producir un mejor C ++ / C / Algol. Sin embargo, para obtener todo el poder de Lisp, necesita todas sus características esenciales, en ese momento tiene otro dialecto de Lisp, una familia distinta de idiomas que ha existido de una forma u otra durante más de 50 años.

Spacebat
fuente