¿Por qué algunos lenguajes de programación funcionales utilizan un espacio para la aplicación de funciones?

34

Después de mirar algunos lenguajes para la programación funcional, siempre me pregunté por qué algunos lenguajes fp usan uno o más caracteres de espacios en blanco para la aplicación de funciones (y definición), mientras que la mayoría (¿todos?) De los lenguajes imperativos / orientados a objetos están usando paréntesis, lo que parece Ser la forma más matemática. También creo que este último estilo es mucho más claro y legible que sin los parens.

Entonces, si tenemos una función f (x) = x², existen dos alternativas para llamarla:

  • FP: f x

    Ejemplos:

    • ML, Ocaml, F #
    • Haskell
    • LISP, Esquema (de alguna manera)
  • No FP: f(x)

    Ejemplos:

    • Casi todos los idiomas imperativos (lo sé, vea los comentarios / respuestas)
    • Erlang
    • Scala (también permite "notación de operador" para argumentos individuales)

¿Cuáles son las razones para "dejar de lado" los paréntesis?

pvorb
fuente
99
Para hacerlo más confuso, erm, quise decir sucinto .
Den
11
@Den si aprendes haskell, la sintaxis será una de las cosas menos confusas: p
Simon Bergot
55
¿Te das cuenta de la ironía de decir que usar paréntesis sería más matemático y luego usar la función de exponenciación como ejemplo?
Jörg W Mittag
12
@Den te estás haciendo daño al rechazar un idioma debido a su sintaxis. Seguramente no tuvo problemas para aprender xml o sql (no son lenguajes de uso general, pero definen su propia sintaxis).
Simon Bergot
77
Me gustaría señalar que los scripts de shell (por ejemplo, bash) generalmente no usan parámetros para llamar a los comandos. PowerShell tiene lo peor de ambos mundos en que las funciones se declaran entre paréntesis y se llaman sin ellas.
Kris Harper

Respuestas:

59

que parece ser la forma más matemática

Los lenguajes funcionales están inspirados en el cálculo lambda . En este campo, los paréntesis no se utilizan para la aplicación de funciones.

También creo que este último estilo es mucho más claro y legible que sin los parens.

La legibilidad está en el ojo del espectador. No estás acostumbrado a leerlo. Es un poco como los operadores matemáticos. Si comprende la asociatividad, solo necesita unos pocos padres para aclarar la estructura de su expresión. A menudo no los necesitas.

El curry también es una buena razón para usar esta convención. En haskell, puede definir lo siguiente:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

Con parens, puede usar dos convenciones: f(5, 6)(no curry) o f(5)(6)(curry). La sintaxis de Haskell ayuda a acostumbrarse al concepto de curry. Todavía puede usar una versión sin curry, pero es más doloroso usarla con combinadores

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

Observe cómo la segunda versión lo obliga a registrar x como una variable, y que la subexpresión es una función que toma un número entero y le agrega 5. La versión al curry es mucho más ligera, pero también es considerada por muchos como más legible.

Los programas Haskell hacen un uso extensivo de aplicaciones parciales y combinadores como un medio para definir y componer abstracciones, por lo que este no es un ejemplo de juguete. Una buena interfaz de función será aquella en la que el orden de los parámetros proporcione un uso curioso amigable.

Otro punto: una función sin parámetros debe llamarse con f(). En Haskell, dado que solo manipula valores evaluados perezosos inmutables, simplemente escribe fy lo considera como un valor que necesitará realizar algunos cálculos cuando sea necesario. Como su evaluación no tendrá ningún efecto secundario, no tiene sentido tener una notación diferente para la función sin parámetros y su valor devuelto.

También hay otras convenciones para la aplicación de funciones:

  • Lisp: (fx) - prefijo con paréntesis externos
  • Adelante: xf - postfix
Simon Bergot
fuente
77
Nitpick menor: los términos son "curry" y "curry", no "currification" y "currified".
Doval
"no curry" ¿Qué es una función no curry en Haskell?
Ven
3
@ user1737909 Una función que toma una tupla como argumento.
Doval
1
@Doval En realidad debería ser "schönfinkeling" y "schönfinkeled". Pero bueno, la historia siempre define al ganador retrospectivamente.
Profpatsch
Pensando en una sintaxis entre paréntesis currificación, algo así como f(a, ,b)o add(a, ), o add(, b)parecería más flexible de manera predeterminada.
phresnel
25

La idea básica es hacer que la operación más importante (aplicación de función) sea más fácil de leer y más fácil de escribir. Un espacio es muy poco intrusivo para leer y muy fácil de escribir.

Tenga en cuenta que esto no es específico para los lenguajes funcionales, por ejemplo, en Smalltalk , uno de los primeros lenguajes e idiomas OO inspirados en él ( Self , Newspeak , Objective-C), sino también en Io e idiomas inspirados en él (Ioke, Seph), el espacio en blanco se usa para llamadas a métodos.

Estilo Smalltalk:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(En el último caso, el nombre del método es replace:with:).

Io-style:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala también permite espacios en blanco para llamadas a métodos:

foo bar(baz)

Y omitiendo los paréntesis si solo hay un argumento:

foo bar baz

Ruby también te permite dejar los paréntesis:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

usando paréntesis, que parece ser la forma más matemática

Realmente no:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

La notación matemática ha evolucionado durante mucho tiempo de una manera terriblemente inconsistente.

En todo caso, los lenguajes funcionales se inspiran en el cálculo λ donde la aplicación de funciones se escribe utilizando espacios en blanco.

Jörg W Mittag
fuente
3
También hay un argumento para la economía de la edición. En particular, los lenguajes funcionales suelen admitir la composición directamente. Poner paréntesis alrededor de la función en lugar del argumento tiene sentido. Solo tiene que hacerlo "una vez": (e. F. G) x en lugar de e (f (g (x))). Además, Haskell, al menos, no evitaría que uno haga f (x) en lugar de f x. Simplemente no es idiomático.
nomen
18

El paréntesis para la aplicación de funciones es solo uno de los muchos sillines que nos dejó Euler. Como cualquier otra cosa, las matemáticas necesitan convenciones cuando hay varias formas de hacer algo. Si su educación matemática solo se extiende hasta una materia no matemática en la universidad, entonces probablemente no esté demasiado familiarizado con los muchos campos en los que la aplicación de funciones ocurre felizmente sin ninguno de estos corchetes sin sentido (por ejemplo, Geometría diferencial, Álgebra abstracta). Y ni siquiera menciona funciones que se aplican infijo (casi todos los idiomas), "outfix" como tomar una norma, o esquemáticamente (Befunge, Topología algebraica).

Entonces, en respuesta, diría que se debe a una proporción mucho mayor de programadores funcionales y diseñadores de lenguaje que tienen una amplia educación matemática. Von Neumann dice: "Joven, en matemáticas no entiendes las cosas. Simplemente te acostumbras a ellas", ciertamente esto es cierto para la notación.

Sean D
fuente
77
Ni siquiera me hagas comenzar con f(x)vs. sin xvs. vs. |x|vs. x!vs. x + yvs. xyvs. ½vs. un resumen vs. un integral vs.…
Jörg W Mittag
2
+1 para la cita de von Neuman. +1 para el resto de la respuesta, pero no puedo dar +2. :(
pvorb
2
Aquí capto una pizca de elitismo; Discuto la neutralidad de "los diseñadores de lenguajes de programación funcional están mejor educados" .
CaptainCodeman
77
@CaptainCodeman Él dice que más educado en matemáticas, una declaración con la que estoy de acuerdo. Al menos cuando se trata de Haskell, se puede notar que ambos conceptos son de naturaleza más matemática y que la comunidad también tiene una mayor inclinación matemática. No son más inteligentes, solo están mejor educados en matemáticas.
Paul
55
@CaptainCodeman No, ya que él nunca dio a entender que son más educados en general, simplemente más educados en matemáticas. Eso es exactamente lo que dijo: "amplia educación matemática". :)
Paul
8

Aunque hay mucha verdad en la respuesta de Simon, creo que también hay una razón mucho más práctica. La naturaleza de la programación funcional tiende a generar muchos más paréntesis que la programación imperativa, debido al encadenamiento de funciones y la composición. Esos patrones de encadenamiento y composición también pueden ser representados sin ambigüedades sin paréntesis.

La conclusión es que todos esos paréntesis se vuelven molestos de leer y rastrear. Probablemente sea la razón número uno por la que LISP no es más popular. Entonces, si puede obtener el poder de la programación funcional sin la molestia del paréntesis en exceso, creo que los diseñadores de lenguaje tenderán a ir de esa manera. Después de todo, los diseñadores de idiomas también son usuarios de idiomas.

Incluso Scala permite al programador omitir paréntesis en ciertas circunstancias, que aparecen con bastante frecuencia cuando se programa en un estilo funcional, por lo que obtienes lo mejor de ambos mundos. Por ejemplo:

val message = line split "," map (_.toByte)

Los parens al final son necesarios para la asociatividad, y los demás se dejan (así como los puntos). Escribirlo de esta manera enfatiza la naturaleza encadenada de las operaciones que está realizando. Puede que no sea familiar para un programador imperativo, pero para un programador funcional se siente muy natural y fluido escribir de esta manera, sin tener que detener e insertar una sintaxis que no agrega significado al programa, pero está ahí para hacer feliz al compilador .

Karl Bielefeldt
fuente
2
"La conclusión es que todos esos paréntesis se vuelven molestos de leer y rastrear. Probablemente sea la razón número uno por la que LISP no es más popular": No creo que los paréntesis sean una razón para que Lisp no sea popular: no creo son particularmente difíciles de leer, y creo que otros idiomas tienen una sintaxis mucho más incómoda.
Giorgio
2
La sintaxis de LISP compensa en gran medida los paréntesis molestos con su alto grado de ortogonalidad. Eso solo significa que tiene otras características de rescate que lo hacen aún utilizable, no que los padres no sean molestos. Entiendo que no todos sienten lo mismo, pero la gran mayoría de los programadores que he conocido lo hacen.
Karl Bielefeldt
"Perdido entre paréntesis estúpidos" es mi backronym favorito de LISP. Las computadoras también pueden usar la forma humana de especificar bloques de código y eliminar la redundancia, como en idiomas como Wisp .
Cees Timmerman el
4

Ambas premisas están equivocadas.

  • Estos lenguajes funcionales no usan espacio para la aplicación de funciones. Lo que hacen es simplemente analizar cualquier expresión que viene después de una función como argumento.

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    Por supuesto, si se utiliza ni un espacio ni parens entonces normalmente no funciona, simplemente porque la expresión no está adecuadamente tokenised

    GHCi> f3

    <‌interactive>: 7: 1:
        No está en el alcance: 'f3'
        Quizás quiso decir 'f' (línea 2)

    Pero si estos idiomas estuvieran restringidos a nombres de variables de 1 carácter, entonces también funcionaría.

  • Las convenciones matemáticas tampoco requieren normalmente paréntesis para la aplicación de funciones. No solo los matemáticos a menudo escriben sen x : para funciones lineales (generalmente llamadas operadores lineales), también es muy habitual escribir el argumento sin parens, tal vez porque (al igual que cualquier función en la programación funcional) las funciones lineales a menudo se manejan sin suministrar directamente un argumento.

Entonces, realmente, su pregunta se reduce a: ¿por qué algunos idiomas requieren paréntesis para la aplicación de funciones? Bueno, aparentemente algunas personas, como tú, consideran que esto es más legible. Estoy totalmente en desacuerdo con eso: un par de padres es agradable, pero tan pronto como haya anidado más de dos, comienza a ser confuso. El código de Lisp demuestra esto mejor, y casi todos los lenguajes funcionales se verían igualmente desordenados si necesitaras parens en todas partes. Esto no sucede tanto en los idiomas imperativos, sino principalmente porque estos idiomas no son lo suficientemente expresivos como para escribir frases claras y concisas en primer lugar.

a la izquierda
fuente
Me tienes, la pregunta no es perfecta. :) Entonces, la conclusión para dibujar es: "Los paréntesis no escalan".
pvorb
2
Tampoco son lenguajes no funcionales que unánimes en que requiere paréntesis; para algunos ejemplos, Smalltalk tiene object method: args, Objective C tiene [object method: args], y Ruby y Perl permiten omitir paréntesis en llamadas a funciones y métodos, solo requiriendo que resuelvan la ambigüedad.
hobbs
1

Una pequeña aclaración histórica: ¡la notación original en matemáticas no tenía paréntesis !

La notación fx para una función arbitraria de x fue introducida por Johan Bernoulli alrededor de 1700, poco después de que Leibniz comenzó a hablar de funciones (en realidad, Bernoulli escribió ϕ x ). Pero antes de eso, muchas personas ya usaban anotaciones como sen x o lx (el logaritmo de x ) para funciones específicas de x , sin paréntesis. Sospecho que esa es la razón por la que muchas personas todavía escriben sin x en lugar de sin (x) .

Euler, que era estudiante de Bernoulli, adoptó Bernoulli de φ x y cambió el griego en una letra latina, escribiendo fx . Más tarde se dio cuenta de que podría ser fácil confundir f por una "cantidad" y creer que fx era un producto, por lo que comenzó a escribir f: x . Por lo tanto, la notación f (x) no se debe a Euler. Por supuesto, Euler necesitaba paréntesis por la misma razón que todavía necesitamos paréntesis en lenguajes de programación funcionales: para distinguir, por ejemplo, (fx) + y de f (x + y) . Sospecho que las personas después de que Euler adoptó el paréntesis como predeterminado porque vieron principalmente a Euler escribir expresiones compuestas como f (ax + b). Raramente escribía solo fx .

Para más información sobre esto, vea: ¿Euler escribió alguna vez f (x) entre paréntesis? .

No sé si los diseñadores de lenguajes de programación funcionales o los inventores del cálculo lambda estaban al tanto de las raíces históricas de su notación. Personalmente, encuentro la notación sin paréntesis más natural.

Michael Bächtold
fuente