Jid – Drill down JSON data incrementally
github.com
github.com
Many people like LISP.
There are reasons JSON is the lingua franca of modern API interaction and LISP is not.
'(
(first-name john)
(last-name smith)
(age 23)
(parents jane jim)
)
{
"first name": "john",
"last name": "smith",
"age": 23,
"parents": ["jim", "jane"]
} '(
(first-name "john")
(last-name "smith")
(age 23)
(parents ("jane" "jim"))
)
Of course, for a pure set of such simply formatted data (hashes, lists, strings, numbers), you could use almost any data serialization format. S-Exs don't really offer anything special here that can't also be done in XML, MessagePack, SQL, protobuff, bencode, thrift, or any of a dozen other serialization formats.Then again, JSON doesn't really bring much special to the table in this case either (other than having an encoder built into most Javascript intrepreters).
The difference between lists and associative arrays is quite unambiguous and readable in JSON. This helps make the language editable by non-expert users, which is a critical benefit.
Strings must be compared byte-by-byte, atoms are interned and compared by pointer identity.
In fact, in some Lisps (such as Common Lisp) you would probably use a special type of atom called a keyword. Keywords are just atoms with a colon at the front to indicate that they belong to the special KEYWORD package; code in two different packages can then compare them without any namespacing fuss. It would look like '((:name "Jane") ...).
Lisp just takes the logic out of the syntax and puts it into the semantics of the language.
'atom' means something else: 'not cons'. Anything that is not a cons cell (the two-pointer building block of linked lists) is an atom: numbers, characters, strings, symbols, arrays, ... Thus a string is an atom, too.
CL-USER 1 > (typep "foobar" 'atom)
T
CL-USER 2 > (typep 4 'atom)
T
CL-USER 3 > (typep 'foobar 'atom)
T
CL-USER 4 > (typep '(foo . bar) 'atom)
NIL
True though, symbols (not atoms) are often used as keys for key/value data structures like assoc lists, property lists, hash tables, CLOS instances and others.Common Lisp also allows symbols to have arbitrary names. It uses | and \ as escape characters in symbols.
CL-USER 11 > '((person |Marvin Minsky|)
(|KNOWN FOR| |Artificial Intelligence Research|)
(lab |MIT AI Lab|))
((PERSON |Marvin Minsky|)
(KNOWN\ FOR |Artificial Intelligence Research|)
(LAB |MIT AI Lab|))
CL-USER 12 > (setf mm *)
((PERSON |Marvin Minsky|)
(KNOWN\ FOR |Artificial Intelligence Research|)
(LAB |MIT AI Lab|))
CL-USER 13 > (assoc '|KNOWN FOR| mm)
(KNOWN\ FOR |Artificial Intelligence Research|)
Symbols are by default interned in a special data structure and are looked up at read-time. Thus they are compared by pointer identity. In Common Lisp this data structure is called a package and there can be more than one package. ("jane" "jim")
is a list, because that's the overwhelming convention in Lisp dialects. A common notation for vectors, used in Scheme and Common Lisp, is the hash-left-paren: #(1 2 3) ;; unambiguously a vector
#() ;; empty vector
There is no standard for notating a hash table; it is dialect specific.For instance, in the Racket language, they use #hash, #hasheq and #hasheqv prefixes: http://docs.racket-lang.org/reference/reader.html#%28part._p... The hash contents are using the same syntax as an association list (list of dotted pairs).
In TXR Lisp, I invented a different notation: a #H prefix which is followed by a compound S-expression. The first element of the S-expression is a list of optional attributes of the hash, and the remaining elements are the key-value pairs. For example:
#H((:equal-based) ("jane" "jim") ("alice" "bob"))
#H(() (:jane 3) (:jim 5) (:alice 7))
equal-based is needed if the keys are aggregate objects like strings, lists and vectors.In Racket these would be:
#hash(("jane" . "jim") ("alice" . "bob"))
#hasheq((#:jane . 3) (#:jim . 5) (#:alice . 7)) ;; keywords are #: in Racket
If you want interoperable S-exps that include literals for associative arrays, you have to pick the way some dialect does it, or roll your own, and customize the reader accordingly.Or else, a decent, portable solution for general data exchange is just to use association lists, like this, which use a notation that is common to many Lisp dialects:
(("jane" . "jim") ("alice" . "bob"))
Though it is not denote an optimized associative array structure, the syntax (at least when it is not empty) clearly conveys that it is associative. The software which processes this can convert it to a hash table, knowing that an associative list is expected for that datum in that position of the syntax. That is to say, we use the representation of an inefficient association list (which, if used "as is" the way it comes out of the parser implies linear searching for keys) and let the applications optimize that as they see fit.Don't rely on first and last names. Also, the age keeps changing, store a date (unless the age is what the user inputs, and is later converted to a date).
(#1=(person ...)
#2=(person ...)
(person
:names ("John Smith")
:date-of-birth @1993-10-04T05:03:00.000000+01:00
:parents (#1# #2#)))Doesn't mean they are.
But yes, I agree with the core of your comment, though I usually harken to XML for a more visceral reaction. First there was a self-formatted schema and tooling to verify it. Then an RPC specification. Followed by parsers built to fit special needs (such as comments). Inspectors, transformers, pretty printers, incremental parsers, bash integrations, namespaces...
Programming history is definitely cyclical.
But this isn't to say that new languages should never be invented. Just that there's a lot more work involved than anyone would expect.
In other words: once you decide you don't want to program in Lisp, Lisp's way of representing data stops being a useful syntax for representing your data. Undervaluing this realization (and worse, reacting smugly when interacting with people who don't share your undervaluing of this) is a big part of the gulf between Lispers and the rest of the world.
Even if you code in C++, you can find Lisp's surface syntax simple enough to read and write. Now, if your language already defines a "read" function, sure, go with it.
The mere fact that a solution exists for a problem does not mean it is the best possible solution for all possible people in all possible cases, nor that there can never be a reason to develop or prefer another solution.
> Undervaluing this realization (and worse, reacting smugly when interacting with people who don't share your undervaluing of this) is a big part of the gulf between Lispers and the rest of the world.
You are the one setting the tone with your exclusionary rhetoric.
Except this is basically how Lisp people react to things. "Oh, why didn't they use Lisp's version of this, it's existed for decades" is a common -- and, let's face it, "Smug Lisp Weenie" exists as a phrase for a reason -- reaction. Lisp people believe Lisp is the greatest thing since sliced bread. It works for them. And that's not a bad thing!
What is a bad thing is the endless haranguing of everyone else about why we won't just do everything the Lisp way. Data serialization formats tend to bring this tendency out pretty badly, as the Lispers show up to essentially ask why anyone bothered to invent anything else after s-expressions.
It's close to the Lisps, Scheme, Racket, Clojure, and many others.
You are correct that the popularity of Ruby, Python, and JavaScript has eclipsed the popularity of these languages, and so the popularity of JSON has eclipsed that of s-expressions.
https://en.wikipedia.org/wiki/List_of_C-family_programming_l...
I don't see where checkbox ticking will get us.
It should be unsurprising that languages which have adopted Lisp's syntax/programming model to a larger degree will also naturally find Lisp's syntax/programming model to work for them, while languages that haven't... won't.
require 'json'
require 'open-uri'
data = JSON.load(open("https://api.github.com/users/Dorian"))
data.keys
data.first
data["public_repos"]
etc.I used to do that in Python, but it is a pain. jq is great for these things, and jid seems to be also.
It's true it's a new lang, and although simple, I often have to look things up.
It might be nice to have a jq REPL, so needn't quote, and easier to keep state around.
For example consider this.
echo '{"users":[{"name":"s1","id":1},{"name":"s2","id":2}]}'|jid
I can query users[0].id or users[1].id. How can I get all the ids? I tried users[*].id which didn’t work.In Clojure I use specter [1] for this which is able to handle wildcards using ALL.
E.g. users[].id
output:
name:s1,id:1
name:s2,id:2Why not?