7 ms·
> s-expressions have no central specification on precise syntax There is: https://people.csail.mit.edu/rivest/Sexp.txt https://people.csail.mit.edu/rivest/Sex
by zeveb 7y ago
> s-expressions have no central specification on precise syntax
There is: https://people.csail.mit.edu/rivest/Sexp.txt https://people.csail.mit.edu/rivest/Sexp.txt
It's imperfect, but it does get the job done. Among its advantages is that it's both pure text and binary: there's a canonical representation which is binary, and an advanced representation which is textual, and they map 1:1 to one another.
Examples:
(session
(capabilities
(urn:ietf:params:jmap:core
(max-size-upload 50000000)
(max-concurrent-upload 8)
(max-size-request 10000000)
(max-calls-in-request 32)
(max-objects-in-get 256)
(max-objects-in-set 128)
(collation-algorithms ("i;ascii-numeric"
"i;ascii-casemap"
"i;unicode-casemap")))
(urn:ietf:params:jmap:mail)
(urn:ietf:params:jmap:contacts)
(https://example.com/apis/foobar
(max-foos-finagled 42)))
(accounts
(A13824
(name "john@example.com")
(is-personal true)
(is-read-only false)
(account-capabilities
(urn:ietf:params:jmap:mail
(max-mailboxes-per-email nil)
(max-mailbox-depth 10))
(urn:ietf:params:jmap:contacts
…)))
(A97813
(name "jane@example.com")
(is-personal false)
(is-read-only true)
(account-capabilities
(urn:ietf:params:jmap:mail
(max-mailboxes-per-email 1)
(max-mailbox-depth 10)))))
(primary-accounts
(urn:ietf:params:jmap:mail A13824)
(urn:ietf:params:jmap:contacts A13824))
(username "john@example.com")
(api-url "https://jmap.example.com/download/{accountId}/{blobId}/{name}?accept={type}")
(upload-url "https://jmap.example.com/upload/{accountId}/")
(event-source-url "https://jmap.example.com/eventsource/?types={types}&closeafter={closeafter}&ping={ping}")
(state "75128aab4b1b"))
Or the binary encoding:
{KDc6c2Vzc2lvbigxMjpjYXBhYmlsaXRpZXMoMjU6dXJuOmlldGY6cGFyYW1zOmptYXA6Y29y
ZSgxNTptYXgtc2l6ZS11cGxvYWQ4OjUwMDAwMDAwKSgyMTptYXgtY29uY3VycmVudC11cGxv
YWQxOjgpKDE2Om1heC1zaXplLXJlcXVlc3Q4OjEwMDAwMDAwKSgyMDptYXgtY2FsbHMtaW4t
cmVxdWVzdDI6MzIpKDE4Om1heC1vYmplY3RzLWluLWdldDM6MjU2KSgxODptYXgtb2JqZWN0
cy1pbi1zZXQzOjEyOCkoMjA6Y29sbGF0aW9uLWFsZ29yaXRobXMoMTU6aTthc2NpaS1udW1l
cmljMTU6aTthc2NpaS1jYXNlbWFwMTc6aTt1bmljb2RlLWNhc2VtYXApKSkoMjU6dXJuOmll
dGY6cGFyYW1zOmptYXA6bWFpbCkoMjk6dXJuOmlldGY6cGFyYW1zOmptYXA6Y29udGFjdHMp
KSgzMTpodHRwczovL2V4YW1wbGUuY29tL2FwaXMvZm9vYmFyKDE3Om1heC1mb29zLWZpbmFn
bGVkMjo0MikpKDg6YWNjb3VudHMoNjpBMTM4MjQoNDpuYW1lMTY6am9obkBleGFtcGxlLmNv
bSkoMTE6aXMtcGVyc29uYWw0OnRydWUpKDEyOmlzLXJlYWQtb25seTU6ZmFsc2UpKDIwOmFj
Y291bnQtY2FwYWJpbGl0aWVzKDI1OnVybjppZXRmOnBhcmFtczpqbWFwOm1haWwoMjM6bWF4
LW1haWxib3hlcy1wZXItZW1haWwzOm5pbCkoMTc6bWF4LW1haWxib3gtZGVwdGgyOjEwKSko
Mjk6dXJuOmlldGY6cGFyYW1zOmptYXA6Y29udGFjdHMzWiYpKSkoNjpBOTc4MTMoNDpuYW1l
MTY6amFuZUBleGFtcGxlLmNvbSkoMTE6aXMtcGVyc29uYWw1OmZhbHNlKSgxMjppcy1yZWFk
LW9ubHk0OnRydWUpKDIwOmFjY291bnQtY2FwYWJpbGl0aWVzKDI1dXJuOmlldGY6cGFyYW1z
OmptYXA6bWFpbCgyMzptYXgtbWFpbGJveGVzLXBlci1lbWFpbDE6MSkoMTc6bWF4LW1haWxi
b3gtZGVwdGgyOjEwKSkpKSkoMTY6cHJpbWFyeS1hY2NvdW50cygyNTp1cm46aWV0ZjpwYXJh
bXM6am1hcDptYWlsNjpBMTM4MjQpKDI5OnVybjppZXRmOnBhcmFtczpqbWFwOmNvbnRhY3Rz
NjpBMTM4MjQpKSg4OnVzZXJuYW1lMTY6am9obgo=}
This is the equivalent of the JSON:
{
"capabilities": {
"urn:ietf:params:jmap:core": {
"maxSizeUpload": 50000000,
"maxConcurrentUpload": 8,
"maxSizeRequest": 10000000,
"maxConcurrentRequest": 8,
"maxCallsInRequest": 32,
"maxObjectsInGet": 256,
"maxObjectsInSet": 128,
"collationAlgorithms": [
"i;ascii-numeric",
"i;ascii-casemap",
"i;unicode-casemap"
]
},
"urn:ietf:params:jmap:mail": {}
"urn:ietf:params:jmap:contacts": {},
"https://example.com/apis/foobar": {
"maxFoosFinangled": 42
}
},
"accounts": {
"A13824": {
"name": "john@example.com",
"isPersonal": true,
"isReadOnly": false,
"accountCapabilities": {
"urn:ietf:params:jmap:mail": {
"maxMailboxesPerEmail": null,
"maxMailboxDepth": 10,
...
},
"urn:ietf:params:jmap:contacts": {
...
}
}
},
"A97813": {
"name": "jane@example.com",
"isPersonal": false,
"isReadOnly": true,
"accountCapabilities": {
"urn:ietf:params:jmap:mail": {
"maxMailboxesPerEmail": 1,
"maxMailboxDepth": 10,
...
}
}
}
},
"primaryAccounts": {
"urn:ietf:params:jmap:mail": "A13824",
"urn:ietf:params:jmap:contacts": "A13824"
},
"username": "john@example.com",
"apiUrl": "https://jmap.example.com/api/",
"downloadUrl": "https://jmap.example.com
/download/{accountId}/{blobId}/{name}?accept={type}",
"uploadUrl": "https://jmap.example.com/upload/{accountId}/",
"eventSourceUrl": "https://jmap.example.com
/eventsource/?types={types}&closeafter={closeafter}&ping={ping}",
"state": "75128aab4b1b"
}
I think it's pretty obvious which one is cleaner …
- shawnz 7y ago> I think it's pretty obvious which one is cleaner … Yes, clearly it's the JSON. How do you tell at a glance in the s-expr formatted data what's an object and what's an array? What's a keyword and what's a literal? I can barely even tell the heirarchy where it's very visually obvious with the JSON.
- frenchyatwork 7y agoI have no strong love for s-expressions, but objects and arrays are programming language constructs, not data format constructs. There's also no way in JSON to distinguish between an int and a float and a decimal type, but that's fine because it's a data format, and not a programming language literal. The JSON and s-expr have a different indent level, which is why the hierarchy is less obvious to you.
- shawnz 7y ago> objects and arrays are programming language constructs, not data format constructs. Debatable. To me there is a clear difference between a list of things of the same type, and a mapping of names to values of potentially varying types. I don't think the specific programming language really matters, they are two totally different concepts with much different applications. > There's also no way in JSON to distinguish between an int and a float and a decimal type True, but they are just small literals which are easy to differentiate at a glance, not big multi-line structures like objects or lists. Plus s-expressions have the same issue.
- zeveb 7y ago> what's an object and what's an array? There are no objects — it's all lists (or arrays, if you prefer). This is a good thing, because ordering is always the same. > What's a keyword and what's a literal? Everything's a byte string: any interpretation above that is a matter for the protocol. This is a good thing, because it means you neatly sidestep issues like JSON's numbers actually being floats rather than integers, as well as issues like numeric precision. 'But I want types!' Of course, they are good. But the type of a number isn't just integer or float: it's integer-which-is-a-known-version or float-which-is-between-zero-and-one-exclusive. You need to check those types at ingestion anyway; it's not a big leap from integer-which-is-a-known-version to string-which-parses-as-an-integer-which-is-a-known-version. > I can barely even tell the heirarchy where it's very visually obvious with the JSON. They are both indicated with indentation. Probably I should have used two spaces for the JSON indentation, but I was lazy.