I think it's a lovely idea, with an interesting implementation.
Obligatory HN style comment: It might be wise to strengthen the description from "pure" functions to "pure and terminating". I've not had time to form an opinion on whether the restrictions are sufficient to ensure this (JavaScript's evaluation rules are...complex). However, the informal specification of this being a lambda calculus with multiple arguments isn't strong enough - that would allow non-termination on load, which is not something you really want in your data serialisation format.
There is no protection against non-termination. For example, an user could give you this function:
f = (function (x){ return x(x); })
Now, if you call `f(function(f){return function(x){ return f(f(x)) }})`, it'll not halt. But there are good principled ways to work around that, such as adding a type system like System F. That would discard 100% of the programs that halt, at the cost of also losing some programs that do halt.
Fun project. From examining the source code, they are using the technique that can be considered a kind of higher order abstract syntax (HOAS). In particular, LJSON can be fooled by so called exotic terms. E.g.
LJSON.stringify(function(v) { return v; })
evaluates to the string
(function(v0){return v0})
While
LJSON.stringify(function(v) { return v(null) } )
Evaluates to the string
(function(v0){return "v0"})
In the latter case, v(null) was able to examine the variable that stringify injects into the function. In this case, v(null) is the name of the injected variable.
Actually there are other problems that are caused by the fact that you're not really doing HOAS, but something even more difficult, namely trying to infer the function by passing in values.
For example
LJSON.stringify(function(v) { if (v == 0) { return "zero"; } else { return "one"; } })
Evaluates to the string
(function(v0){return "one"})
The only way to avoid both these issues, is to inspect the code of a function f using f.toString(). However you would need to consider the scope where f was defined.
Ah, fair enough. The user should avoid using native JS constructs on the code to be stringified, though - it is an interesting/useful side effect of the method that you are able to, but ideally you would define your functions using only the pure λ-calculus grammar.
Without being able to call pure functions within pure functions, what use is this beyond the trivial simple examples? Couldn't it be possible to pass a "this" argument as the current JSON node and have it traverse the tree or something? For example:
There are many possibilities since you can pass any program/algorithm through LJSON while still having a good level of safety. You could it, for example, to allow your users to pick an AI strategy for their bots in a game, among billion of other things. But there are many restrictions, so you can't just write arbitrary JS functions. You have to design them much more carefully by requesting the proper primitives, using the technique I explained with the hypothenuse example. Of cours, you could just request `window` as a primitive and get everything, but yea...
I added a file, `examples.js`, which shows how you can encode some array algorithms on λJSON. It may look extremely inexpressible due to so lack of loops, math, etc - but it is still able to encode any conceivable algorithm, although more carefully.
This would be interesting, except JavaScript is a terrible language for this. At the minimum I think you'd want all the arguments and return values to be strictly typed. Otherwise you can't for example determine how much memory to allocate for the returned value. The big strength of JSON is that the encoder and decoder can be written by anyone in any language fairly easily. I've used it in JS, Python, PHP, and C, and every time it's been super easy because the format is so simple.
What would actually be cool is to add type hints to JSON. That way we could conceivably add things like date types to it fairly easily.
This is something I'd wanted to do, and I'm glad someone else had the same idea. Lots of exciting possibilities.
Something important that needs doing is strictly defining the JavaScript subset used. As simple as possible, ideally, in keeping with the JSON spirit. This would mean you could then safely implement that subset in other languages. Imagine serialising a PHP function into λJSON and running it from JavaScript, or vice-versa!
There are no free terms in any of these LJSON functions, since all the terms are explicitly named in the arguments. Globals would presumably get shadowed and so would not be captured.
Obligatory HN style comment: It might be wise to strengthen the description from "pure" functions to "pure and terminating". I've not had time to form an opinion on whether the restrictions are sufficient to ensure this (JavaScript's evaluation rules are...complex). However, the informal specification of this being a lambda calculus with multiple arguments isn't strong enough - that would allow non-termination on load, which is not something you really want in your data serialisation format.