# Why JSON and other languages should allow more flexible separators

DevFeed: [Why JSON and other languages should allow more flexible separators](<https://devfeed.tech/articles/nontrailing-separators-do-not-spark-joy-25499.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/nontrailing-separators-do-not-spark-joy/>)

Author: Hillel Wayne

Published: 2026-06-10T12:22:04Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [JSON](<https://devfeed.tech/topics/json.md>), [Haskell](<https://devfeed.tech/topics/haskell.md>), [Parsing](<https://devfeed.tech/topics/parsing.md>)

Tags: [json](<https://devfeed.tech/tags/json.md>), [language](<https://devfeed.tech/tags/language.md>), [standard](<https://devfeed.tech/tags/standard.md>), [transformation](<https://devfeed.tech/tags/transformation.md>)

## AI overview

The article argues that requiring separators in particular positions makes common code transformations harder. Using JSON as the main example, it compares trailing, leading, and both-sided separator styles across languages including Haskell, TLA+, Prolog, Python, Go, and Alloy.

## Source excerpt

This is valid JSON: { "a": 1, "b": 2, "c": 3 } This is invalid JSON: { "a": 1, "b": 2, "c": 3, } The difference is the last comma. The JSON grammar specifies that a comma can separate two members of an object but not postcede ("trail") a member. I think this was a design mistake. Say we want to add two new keys to the struct, one before the "a" member and one after the "c" member. Here's what it would look like if trailing commas were permitted: { + "x": 0, "a": 1, "b": 2, "c": 3, + "y": 4, } It's the exact same text transformation regardless of where we add the key. In the current model, we instead have this: { + "x": 0, "a": 1, "b": 2, - "c": 3 + "c": 3, + "y": 4 } Those are different transformations! Similarly if you want to remove an element, you can't just delete the corresponding line1, you have to delete the line and then check that the last line doesn't have a trailing comma. Don't even get me started on all the special cases involved in swapping two lines. JSON isn't the only language with this problem. Haskell writes record types like this: -- from https://play.haskell.org/ data Drone = Drone { xPos :: Int , yPos :: Int , zPos :: Int } This "partial bullet point" style of putting separators at the beginning of rows makes it easier to change the last row but harder to change the first one. TLA+ has this problem too: \* both valid VARIABLES a, b, c vars == <<a, b, c>> \* both invalid VARIABLES a, b, c, vars == <<a, b, c,>> This one's annoying because 1) you're constantly adding new top-level variables while working on a spec and 2) the PlusCal DSL does not have this problem: \* Totally fine! (*--algorithm foo { variables a; b; c; The worst offenders, IMO, are logic languages like Prolog. Not only don't you have trailing separators, you have a special terminating symbol: foo(A, B, C) :- A = 1, % comma B = 2, % comma C = 3. % period! I guess you can sort of think of it as funny-lookin' braces: foo(A, B, C) :- A = 1, B = 2, C = 3 . But this is not standard synt