- En las estructuras de datos, si se permiten los separadores finales, agregar, eliminar o reordenar elementos se puede manejar con el mismo tipo de cambio de texto
- JSON prohíbe la coma después del último miembro, así que al agregar o eliminar una clave al final aparece un caso especial que obliga a modificar también la línea existente
- Los registros de Haskell, las declaraciones de variables en TLA+ y las reglas de Prolog también hacen que los cambios en la primera línea y en la última se manejen de forma distinta por la posición del separador o el símbolo de terminación
- Python y Go permiten comas finales, pero no comas iniciales, mientras que Alloy permite tanto comas iniciales como finales
- Los separadores finales pueden crear ambigüedad de parsing en estructuras de control y también se usan para distinguir significado en sintaxis de datos, como en las tuplas de un solo elemento en Python
El problema de la coma final en JSON
- En los objetos JSON se permiten comas entre miembros, pero la coma después del último miembro no está permitida por la gramática
{
"a": 1,
"b": 2,
"c": 3
}
- En ese mismo objeto, si se agrega una coma después del último miembro, como en
"c": 3,, el JSON deja de ser válido
{
"a": 1,
"b": 2,
"c": 3,
}
- Si se permitieran comas finales, al agregar
"x" antes de "a" y "y" después de "c" solo haría falta añadir líneas con la misma forma
{
+ "x": 0,
"a": 1,
"b": 2,
"c": 3,
+ "y": 4,
}
- Con la gramática actual de JSON, al agregar una clave en la última posición también hay que poner una coma en la línea final existente
"c": 3, lo que hace el cambio más complejo
{
+ "x": 0,
"a": 1,
"b": 2,
- "c": 3
+ "c": 3,
+ "y": 4
}
- Al eliminar elementos tampoco basta con borrar esa línea: hay que comprobar que no quede una coma final en la última línea
- Si el valor del objeto es en sí un arreglo u objeto de varias líneas, la transformación se vuelve más compleja por la ausencia de coma final
Casos similares en otros lenguajes
-
Registros de Haskell
- Haskell permite usar en los tipos de registro un estilo de “viñetas parciales” donde la coma va al inicio de cada línea
data Drone = Drone
{ xPos :: Int
, yPos :: Int
, zPos :: Int
}
- Este enfoque facilita cambiar la última línea, pero hace más difícil cambiar la primera
-
TLA+
- En TLA+, una lista de variables o una secuencia sin coma al final es válida
VARIABLES a, b, c
vars ==
- En esa misma sintaxis, si se agrega una coma después del último elemento, deja de ser válida
VARIABLES a, b, c,
vars ==
- Al escribir especificaciones en TLA+, es común seguir agregando variables de nivel superior, así que esta restricción resulta incómoda
- En el DSL PlusCal no existe el mismo problema, y las declaraciones de variables se pueden listar con punto y coma
(*--algorithm foo {
variables a; b; c;
-
Prolog
- Los lenguajes lógicos como Prolog no solo no permiten separadores finales, sino que además usan un símbolo de terminación distinto
foo(A, B, C) :-
A = 1, % comma
B = 2, % comma
C = 3. % period!
- Se podría ver el estilo de poner el punto final en una línea aparte como algo parecido a las llaves, pero no es la sintaxis estándar y tampoco resuelve el tema de los separadores finales
foo(A, B, C) :-
A = 1,
B = 2,
C = 3
.
Una mejor forma
-
Lenguajes que permiten separadores finales
- Go permite una coma después del último elemento en los literales de mapa
valid := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
- Python también permite una coma después del último elemento en los diccionarios
valid = {
"a": 1,
"b": 2,
"c": 3,
}
- En Python y Go la coma puede ir después, pero no antes, así que no se puede lograr un estilo completo de viñetas
invalid = {
, "a": 1
, "b": 2
, "c": 3
}
-
Separadores iniciales y Alloy
- TLA+ permite conjunción con prefijo y disyunción con prefijo, pero no permite la forma posfija como
(a &&)
// Not TLA+ but the same semantics
|| && a == 1
&& b == 2
|| && a == 3
&& b == 4
- Alloy permite tanto comas iniciales como comas finales
sig Valid {
, a: 1
, b: 2
}
sig AlsoValid {
a: 1,
b: 2,
}
- Alloy también permite separadores vacíos, por lo que incluso líneas con solo varias comas siguen siendo válidas
sig StillValid {
,, a: 1,,
,,,,,,,,,
,, b: 2,,
}
- A este tipo de forma algunas personas la llaman “stuttering”
Contraargumento: ambigüedad de parsing
-
Separadores de control en Prolog
- Uno de los argumentos contra los separadores finales es que pueden volver ambiguo el parsing
- En Prolog, si una regla termina con un punto, queda claro que
foo y bar son definiciones separadas
foo(A, B) :-
A = 1,
B = 2.
bar(c).
- Si el símbolo de cierre de la regla se cambiara por una coma,
bar(c) podría interpretarse como parte de la definición de foo
foo(A, B) :-
A = 1,
B = 2,
bar(c),
- En ese caso,
foo podría interpretarse como verdadero solo cuando bar(c) también sea verdadero
-
Llamadas a métodos en Ruby
- En Ruby se puede seguir una cadena de métodos incluso después de un salto de línea, y el siguiente código imprime 5
puts 3.
succ().
succ()
- Si se permitieran separadores finales después de llamadas a métodos, no quedaría claro si
quux() es una función de nivel superior o un método de foo
foo.
bar().
baz().
quux()
- Los casos de Prolog y Ruby se relacionan con ambigüedades en separadores de control, no con separadores de datos
Excepción en sintaxis de datos: tuplas de Python
- Python usa paréntesis tanto para agrupar expresiones como para definir tuplas
(2+3) se trata como evaluación de una expresión y produce un int
>>> x = (2+3)
>>> type(x)
(2+3,) se trata como una tupla de un solo elemento por la coma final
>>> x = (2+3,)
>>> type(x)
- En este caso de Python, el separador final de datos sirve para distinguir entre una expresión y una tupla de un solo elemento
1 comentarios
Comentarios en Lobste.rs
La gramática de JSON dice que puede haber comas entre dos miembros de un objeto, pero no una coma final después de un miembro. No creo que se le pueda llamar un “error de diseño”, porque no era una opción JSON se creó alrededor de 2000–2001 como un subconjunto de ECMAScript 3, y el RFC 4627 informativo se escribió en 2006. Como era un subconjunto de JavaScript, el objetivo de JSON —y una clave de su éxito— era que funcionara directamente en el navegador con
eval; la API nativa de JSON en navegadores recién se añadió en 2009 La ES5 recién especificó las comas finales en diciembre de 2009, así que un JSON con comas finales simplemente no podía existir sin contradecir su propósito originalEn Prolog, esta es una de las molestias que más seguido me encuentro. Cuando trabajo con un predicado, me resulta cómodo dejar
,true.al final, así no tengo que preocuparme por el punto final al reordenar o comentar líneas de arribawhere true / and ... / and .... Aquí la barra significa salto de línea Así cualquier condición se puede editar fácilmente sin darle un trato especialZig permite la coma final, y se puede usar para controlar un formateador no configurable
.{1, 2, 3,}se convierte en estoY si quitas la coma final en un literal formateado en vertical, pasa a significar que pides alineación horizontal:
.{ 1, 2, 3 }También funciona aquí: definiciones de tipos contenedorstruct { a: u32, b: u32, }, firmas de funciónfn foo(a: u32, b: u32,) void {}, llamadas a funciónfoo(1, 2,);En todos esos casos puedes controlar el autoformato con la coma final Me gustó tanto esta función que la añadí también a mi servidor de lenguaje/formateador automático de HTML Before:After:
https://github.com/kristoff-it/superhtml
Coincido 100%. Personalmente, un lenguaje nuevo sin separadores finales me baja un poco la nota. No al punto de odiar su gramática, pero sí se siente como una pequeña molestia persistente
foo(1,2,3,4,)?bar(,1,2,3,4)? Sifoo()permite una cantidad variable de parámetros, ¿el último argumento esnil? ¿El primer parámetro debar()esnil?En Clojure y EDN, la coma es espacio en blanco. Normalmente se usa por convención entre pares clave-valor en literales de mapa en una misma línea, pero es totalmente opcional
Creo que una gran parte de la tensión aquí viene de que cuando hay varias cláusulas en una misma línea, de algún modo hace falta un separador
En esos casos, agregar o quitar argumentos según diferencias por línea casi siempre se ve incómodo. Incluso comentar un solo argumento requiere algo de cuidado y esfuerzo. A cambio aceptas la concisión de poner varios elementos en una misma línea Pero cuando empiezas a dejar una sola cláusula por línea, idealmente quieres diferencias más limpias y una manera sencilla de desactivar cosas con comentarios de línea simples La respuesta obvia es tratar los saltos de línea como separadores estándar
Muchos lenguajes hacen esto con
;. Si tu estilo desalienta poner varias sentencias en una sola línea, casi no llegas a ver;. No conozco lenguajes que hagan exactamente lo mismo con comas, pero sí es algo que alguna vez quise probar en un lenguaje de hobby Claro, Lisp evita este problema porque las subexpresiones y subcláusulas siempre están completamente delimitadas, así que no hay separadores en absolutoEsta es una de las razones por las que me gusta Lisp, o más precisamente las S-expresiones. Es un detalle menos en el que pensar