And the circle of life continues! I have seen exactly this method in 2004-2006 but with XML.
If you are reading this, and you think it is a good idea, please don't follow it as it is not.
Back in 2006-2009 I worked at MobiTV, which we had the same exact way/method of programing our mobile apps, on J2ME/feature phones. The made up language was called CEF which was based on XML. While the idea is sound in paper, This turns really fast into an ugly, debug everywhere type of scenario, where it is hard to both develop and debug properly.
The only way this is ok, is when you have some fully automated UI tool, that stores some kind of state, and retrieves it back, but with no human intervention and some very very limited logic. Otherwise you are in a world of pain. If you try to include real application logic into something like this it is going to hurt your business long term.
It will turn your business logic like some Maven configuration, full of gotchas and more.
However, something I’ve come to learn is that some ideas I thought were bad were actually not bad. They were poorly executed and the exact reasoning why they did not work well in a given application was not well-understood.
So I can see an argument for “this was horrible back when it was done in the early 2000s” simultaneously with “this could actually still be a good idea for some use cases.”
Currently reading about agriculture throughout human history. It made me wonder how many people failed to domesticate a single kind of crop and assumed crop domestication as a whole isn’t a good idea.
I realize that people need to make decisions based on their personal experience, and I don’t have a good rule-of-thumb for when to try a variation of a “bad” idea.
Agriculture is difficult because it is literally a fight against nature/entropy. And it still has to be proven that it is a viable way to drive human civilization in the long term.
In nature, species populations compete with each other and are kept in a dynamic equilibrium. If population size of a species increases, then its predators will have more opportunities to predate, and their population size increases as well. This leads to more predation pressure, which should negate the population increase and decreases opportunities for predators as well. "Predators" is to be understood in a very general way. It can also apply to fungi and other microbial forms of life.
Changes to this equilibrium arise when prey species evolve to resist the predation pressure by predators. And vice versa if the predators improve. Then, ecosystems change, and these changes can be very disruptive.
Agriculture is a rebellion against this dynamic equilibrium because it creates irresistible opportunities for predators. They have to be held in check either by brute force (hunting, pesticides, containment) or by hacking the ecosystem (for lack of a better term) to ensure that helpful predators are around. The former is probably unsustainable in the long term as it is not at all clear that we will forever be able to outsmart the evolutionary potential of predator species. The latter is difficult as well.
Apart from predation, the other challenge to agriculture is that it depletes the soil in the long term. Fertile soil arises as a result of a ecosystem where the decomposers cannot keep up. Most forms of agriculture interrupt such ecosystems and thus deplete the soil in various ways.
To keep this in check, different methods were evolved, such as slash-and-burn, crop rotation and artificial fertilization. But it is not at all sure that we will always be able to stop depletion of our soils just in time to prevent civilizational collapse. Currently, availability of phosphorus is a looming danger because most of it is mined. Once we hit peak phosphorus, we better have solutions in place to get it from somewhere else.
It was possible and even made sense to use XML as the format for a build tool, however, it got stretched way to far. The idea was applied in use cases that were less and less appropraite, as a result years later you end up with something that is unwieldy and doesn't make sense and, generally, thought of as "terrible".
There are use cases were JsonLogic might make a lot of sense, and for those that is great.
Generally, I'm sceptical as there is high risk of increasing couplings in a way that will make change much harder. This will be particularly true if it is applied to use cases that don't truly warrent this kind of solution.
Sharing code between frontend and backend isn't a bad idea, yes. However, using Json for it is. And the problems and limitations with json can't be "fixed" in any way.
I think there are very good reasons why languages have their own custom formats and none of them ever decided to serialize into json, even when just "generating it".
But what reasons and are they relevant to this use? For instance, I can think of one really good reason (actually several different ones justifying different formats getting used depending on the actual use case, but they can all be summarized the same way when it comes to not using JSON): Shipping an AST between endpoints one of which is a web page and therefore has JSON as its best supported generic object serialization format isn’t the use case.
No I don't think there is a good reason. You're completely missing that programming language syntax is designed for humans. You can still serialize all languages to json syntax trees, just like you can do so with lisp notation, Polish notation etc.
I'm not saying that you _cannot_ do it. But tell me: why has no programming-language done that? They all could have chosen to serialize their code or AST into json but they didn't. (except JsonLogic I guess)
People did do it (JsonLogic by your own admission) and similar things (every single Lisp based language every) so if your only argument is "people didn't do it before, therefore it cannot be a good idea" it doesn't work.
Because someone didn't have the time to do it? Or they didn't think of it? Just because nobody has had success at an idea, doesnt mean that the idea can't work.
Unfortunately, it has taken me 40 years to learn that most people have no clue about innovation. I mean, imagine believing there are no good ideas left, all the good songs have been written.
From now on, when someone tells me something is a bad idea, I'll take it mean that I should examine it further.
One valid use case for solutions like this is if users are allowed to run their queries inside a database or api.
I'm often in the situation where you try to add all kinds of parameters to a query-field in the UI but no matter how hard you try there will always be a power user with the request to do some combination of AND/OR/CONTAINS/NOT with nested parenthesis that is not supported.
After hundred such requests you get sick of it and just want to expose the raw DB-query to the front end, which obviously is a bad idea for security reasons. Having a safe intermediate representation that is sophisticated enough for some logic can be a good idea. Instead of reinventing your own, using this language can be done, as long as it has a mapping into each database query language.
> there will always be a power user with the request to do some combination of AND/OR/CONTAINS/NOT with nested parenthesis that is not supported.
If that power user is properly skilled, authorized and authenticated, why not give them a read-only connection to the DB and let them write their own SQL?
How many times has the UI been nothing but an impediment?
I dunno, to me it's pretty clear that JSON has improvements over XML; XML:
- does not entirely make it clear what should go into a tag attribute versus a nested tag
- conversion of those two features into a unified internal datatype can be tricky (for example, this property led to choices that for erlang's xmerl causes a security regression if you are handling user-submitted xml).
- first class support for text inside of a tag is not what you want in 90% of data interchange events, and support for it creates a burden when deserializing.
- it's confusing what should be a text inside a tag versus a tag attribute. Or should that text be parsed into a number? At least JSON is strongly typed, you know what datatype is what just by looking at it.
- because there aren't good "best practices" around it, everyone rolls their XML schemas with their own ideas about what sort of thing should go where.
The major benefit of XML is its obession with schematization means that at least in many case you can grab a schema and figure out an appropriate declarative command to obtain what you want (if you have the right library), but this has basically been conquered in JSONland using JsonSchema/OpenAPI, and stuff like JsonLogic.
- JSON has just one escape format and all unicode codepoints can be used. XML generally allows only certain unicode codepoints, at some places like element names its further restricted (Quick: Which characters are not allowed? What's the difference between XML 1.0 and 1.1 here?).
XML escapes at every place differently. In comments you need to escape less than elsewhere. In attributes you can use newlines but they will get normalized to spaces, you have to hex-encode newlines. There's Cdata.
- JSON doesn't have the entity madness with potential security issues.
- JSON doesn't have namespaces that add a lot of complexity with little benefit
> - it's confusing what should be a text inside a tag versus a tag attribute. Or should that text be parsed into a number? At least JSON is strongly typed, you know what datatype is what just by looking at it.
JSON's types are pretty anemic compared to the XMLSchema's datatypes[0]. Even it you don't use all of XSD's complex type system, having these basic datatypes is extremely useful and I wish JSON had similar.
JSON can't express ordering or duplicate fields in a consistent way.
Consider how you'd write these 2 bits of XML in JSON:
<A><B>foo</B><C>bar</C></A>
vs.
<A><B>too</B><C>bar</C><B>uh oh</B></A>
Without a schema, in the first case, how do you determine whether B should be "foo" or ["foo"]? In the second how do you preserve the relative ordering of B #1, C and B #2?
I worked on a project where we had a parse tree that we wanted to serialize out, and for this reason XML was the natural choice.
So this is exactly a symptom of unclarity of the roles that xml causes in the first place. It seems like You're trying using the tags as keys instead of as information categories. In json you might encode these sorts of data as something like (pardon my lack of quotes, I'm on mobile):
But generally you shouldn't have a mixed type list in json unless it's a deliberate choice, the "limitations" of json are exactly what devs want because it corresponds to a clarity of thought and purpose that simply isn't there in XML.
In my case the parse tree had nodes that corresponded with grammar rules (mapped to XML elements) and nodes that corresponded to the preserved but otherwise uninteresting fluff that surrounded them (mapped to XML PCDATA).
We wanted a parse tree that could be serialized out to produce the original document byte for byte, but still reflected the grammar.
Things get uglier for JSON in the real world case:
The XML output is essentially the original input + grammar markup. Your solution also works but is a data structure representation with a lot more line noise for humans to read.
It just looks bad because I made it one line; by comparison if I hadn't seen the pre-parsed form 5+7 I would not be able to immediately grok what is going on in your parsed one-line XML, either; there's a lot of "line noise" there too. Moreover it feels like the XML you have has lost the structure of the meaning, by having the tokens 5, +, and 7 be linearized. You probably want a tree with + at the root and 5 and 7 as leaves. That XML encourages these sorts of "oddly flat" representations is one of its weaknesses.
+ at the root would indicate an abstract syntax tree rather than a concrete or "full" syntax tree (terminology varies slightly)
This was primarily a mechanism to load parsed documents in to code that didn't have access to the recursive descent parser that parsed the document, for either security or performance reasons.
Loading the XML with the appropriate parse flags, doing a pre-order traversal on all the leaf PCDATA, and then printing it out, would regenerate the original document byte for byte.
Perhaps I should go back and implement JSON output and see how I feel about it... thought I'm pretty sure once you introduce a bunch of "text" and "type" fields to the JSON tree it's going to look a lot less readable than XML markup.
There are other fields as well like line and column numbers (output as XML attributes)
While I agree that XML has muddy-thinking whether tags are types within a collection or properties of an object, I've often felt that Json's inability to store heterogeneous object arrays meaningfully is a big problem. The lack of a meaningful "type" attribute in JSON schema is a pain.
> Without a schema, in the first case, how do you determine whether B should be "foo" or ["foo"]? In the second how do you preserve the relative ordering of B #1, C and B #2?
Well, if you are literally developing a model for recasting general XML in JSON, then you know the children of an entity should be a list because duplication os possible and order is signifcant. (Given the close relationships between XML and HTML and between JS and JSON, the obvious starting point for a general solution is to copy the HTML DOM, using only the properties.)
If you are trying not to reserialize XML generally but to solve the same problem as the original XML, then you probably aren’t doing it without a (logical) schema—you know what the object model is that you are trying to stuff into JSON, and you decide based on that.
This looks like line noise to me. All that punctuation and quoting looks ridiculous. What is the problem with straightforward and trivial to parse things like
Not to me; its a fairly standard and natural JSON pattern. It’s tue that it use is usually quite limited in any given representation, but usually the thing you are modelling in JSON isn’t general XML with no further knowledge of application domain or logical schema.
I like JSON a lot more than XML for what JSON is used for. XML did have some advantages, like more standards for schemas and templating, but the JSON ecosystem has at least decent facsimiles for these facilities. But the real star of the show is the data model: XML is a markup language. Jamming data structures into it is awkward. Not so for JSON, which has two aggregate types, a list type and a map type, followed by the usual suspects of primitive types: string, number, boolean, null.
I can implement a full, strict, compliant JSON parser in an afternoon, maybe even less time. It is simple enough to parse that you can even do it in pure C with relative ease. XML is a lot more complex and a lot of implementations skimped pretty hard on the details. I mean I have fond memories of tinyxml2, and if you take apart any app made in the 2000s you have a good chance of finding reference to it (or on Windows-only apps, MSXML3, or on Linux, libxml...) but it certainly let you do a lot of surprising things. I saw a lot of apps doing XML documents with multiple root nodes. And Apple's plist format is a nightmare. It's so easy to fuck up interpreting XML plist, that Apple did, and it lead to one of the more entertaining privilege escalation bugs in iOS history.
I have some fond memories, but I just don't miss XML. :)
If you actually look at this, the results are not nearly as bad as title suggests.
There are are many parsers which perfectly parse every valid JSON document (like every Perl, Python and PHP parser and many others). They got non-perfect score because they accepted some JSON which standard says they should not have -- like a NaN value or a trailing comma.
While using such parsers can potentially cause problems -- perhaps because your data generator has a bug, and your not-strict-enough parser ignores this bug -- this happens very rarely in practice.
... but I will say that it is intentionally very limited in its API due to its intended use case and desired footprint. (And because it’s designed to compile with OpenWatcom 2 with no libc for only Win32, but those particular constraints are not very interesting for this.)
I would imagine if I did try, I would have a fighting chance at making something properly strict. But I admittedly don’t know how to handle recursion in a stackless manner.
Edit: specifically for C, it would be hard to do it properly unless I had access to an implementation of Clinger and Steele & White. Go would be easier since it has better floating point algorithms with strconv, but relying on C strtod/etc. is probably a bad idea due to variability. That having been said, maybe you could do Clinger in the same afternoon; I’d be worried about edge cases, but simply put, I haven’t tried.
I am not a programmer, and I have written one over the course of a week. It passed all the tests in the particular JSON test suite mentioned in the article except one, by design. It allowed bad unicode in parts of the document, because that was the data it was being fed.
I am not even a programmer by profession. Anyone that can write a recursive descent parser can write a JSON parser.
One thing I miss from XML is namespaces. Made it easy to extend an existing format with some custom extensions, with the likelihood that tools that didn't know the extensions would just ignore them, and that your extensions wouldn't conflict with anybody else's or with future versions of the underlying format.
I haven't seen any widely-adopted equivalent in JSON. I've seen some proposals floating around but none of them seem to have really taken off.
God no. No one handles XML namespaces well. They are an absolute nightmare to deal with in almost every language.
We already have this in JSON: add a hashmap key with a prefix. It's at least as robust provided tools do the sensible thing and ignore keys they don't care about. But as with XML, sometimes, they don't, or do, or something.
Maybe I'm paranoid, but I worry someone else will pick the same prefix as me. If you make the prefix something like a domain name, e.g example.com:myExtension, nobody else is going to use the same prefix assuming you own example.com. That avoids the risk, but it can be a lot of typing. Whereas, if the prefix is something short and obviously meaningful (e.g "doc:"), there is a risk someone else will use the same prefix in a conflicting way.
What I liked about XML namespaces is the indirection: the prefixes can be whatever you want, and then what makes them unique is the URIs they are defined to map to in each document.
The one point at which I think it didn't work very well, was when people started using prefixed names in attribute values. You can't tell whether "foo:bar" is a reference to the XML namespace with prefix "foo:", or some unrelated string of "foo:bar", without looking at the schema.
In practice though this doesn't really happen: nothing stops someone coming up with the same seemingly unique namespace URI, or using the same namespace prefix.
But none of that is really the problem - the problem is that the implementations and parsing are evidently sufficiently difficult that people just don't get them right - hence the hassle.
I would argue also that having indirection like this is just a negative all around - having to look elsewhere in the file to figure out what I'm looking at just creates problems, and in practice it doesn't look like this capability was ever really used - the only times I run into it are when I'm dealing with some vendor-specific implementation (that Python or Go or whatever can't handle so string-templating winds up being easier) and it turns out that what I need to have happen is to match the semantics of example XML exactly to get anything to work.
Which is really the point: the extra complexity in parsing means the probability you're dealing with a broken parser shoots through the roof, at which point you've lost any advantages. Whereas with JSON, the minimum bar is arrays and maps and the basic types work. Stay within those bounds, and whatever namespacing that happens will "just work".
> nothing stops someone coming up with the same seemingly unique namespace URI
If people use URLs, then DNS gives uniqueness to URLs. Of course each registrant has to enforce uniqueness within their registered domain – for a standards organisation or large software vendor, they often adopt a formal system (spreadsheet, database, naming convention, etc) for tracking which parts of the namespace under their domain name are reserved for which products/standards/etc. Of course, that relies on the vendor/organisation to do it properly – but if (e.g.) Microsoft were to create a mess by letting different Microsoft products use conflicting namespace URIs under microsoft.com, well that would be Microsoft's fault, and not the responsibility of any standard or technology to fix it.
(There is still one issue–what if the domain changes ownership? Some standards, for example ISO 19770, have tried to address this by constructing namespaces based on a domain name with its registration date, so if the domain later expires and is reused by a different organisation they won't clash since they'll have a new registration date.)
> the extra complexity in parsing means the probability you're dealing with a broken parser shoots through the roof
A lot of that is because namespaces were added to XML later, rather than being there from the start; they are effectively an optional feature. So then you have some parsers that support them, some parsers that don't, some parsers where it depends on a flag you have to set (which may default to true or false). I guess trying to add namespaces to JSON now would encounter the same problems. But if someone was to design a new serialisation format from scratch, and had namespace support in it from day 1, I don't think these issues would happen.
I've worked with Will on a large XML based system. I'll try to briefly describe our workflow. Our company created reference books. Those books ended up both as printed books and as HTML in a web application service people could subscribe too. Various people would create the content, some in WYSIWIG editors, others editing raw html to tag content to give it structure, and tools that fall somewhat in between. XML tags and annotations can do things like indicate where the title is, sub headings, links to other books or content. etc etc.
This annotation structure not only lets us transform the xml into HTML that resembles the physical book via CSS, but also lets us search the content with awareness of what the content is. Simple example, if a search term appears in a title give it more weight.
When a book is done the XML goes off to a printer to become a book, AND into our XML database. A nice feature of which is a lot of our content is in the DB as XML, you ask for a book page and the DB returns the HTML for the page, backend just forwards that HTML to the browser which displays it. ZERO parsing/transformation anywhere once the DB sends it! Just fling the payload at the browser. Super cool at times.
Anyway we all work with these formats differently, some of us never touch the json and xml, some of us only tweak values in config files. Those of us who work with it like that, the only differences between XML and JSON are "how nice are the serialization apis and how fast are they? how big is the payload?"
But when working WITH the format directly there can be more meaningful differences. And the tools that actually exist for the things you are doing may push you towards one format or the other, even if in principle it wouldn't have really mattered.
note: I keep mixing tenses here but this was all in the past, we got bought, don't do any of this anymore!
Gotcha, I was thinking more in terms of JSON as a serialization / interchange format (given the OP), rather than use cases such as the one you describe above.
To clarify: no, I do not believe that JSON is universally better than XML. I believe JSON is better than XML for the majority of use cases where people are currently using JSON today, esp. as an interchange format.
Please keep your patronizing comment to yourself, or add some arguments to it.
JSON's spec is simpler, and the data are more compact. There are a ton of extremely fast parsers, as well as stream parsers. Many languages support JSON natively making it extra easy for interoperability.
Pretty sure that was in response to you making assumptions about their experience, not as an answer to the question about what specifically makes them think that, which you only asked afterwards.
This whole chain of comments is a good example of why responding with a dismissive short comment isn't a good idea, even if it feels good to send in the moment.
With equal samples in the two formats, JSON wins on readability. I can draw up bad examples in both. Since you've seen back JSON already, I give you real-world XML (I actually saw this example, yes), describing whether a structure has a roof, or not.
(Normally, exactly one of either the <true> or <false> tags should have a "1", and the other a "0". Normally.)
(The really horrible thing was that there were … I don't want to call them "reasons" because that's a strong word, so let's say "cause and effect" that led to this format… and once you came to know them, you "understood", in a sort of horrible sense, the format. But give me a JSON bool any day over it.)
You don’t state your reasons for this very bad XML, but your comment suggests to the uninformed that XML doesn’t support Boolean types, which isn’t the case.
XML doesn’t support booleans. XML Schema, one of several separate add-on schema languages for XML does, but comparing XML + XML Schema to bare JSON does not make sense.
XML Schema isn't only for validating XML post-hoc, as you suggest. The point is that if you are using XML, it doesn't make sense to invent arbitrary solutions when there is already first class support in standardized toolchains. Comparing the markup specs, specifically, is an unreasonably narrow comparison. The discussion is about "using XML" and "using JSON", and the environments in which they're used are at least as important as the specs, independently.
If you are somehow in an environment where you are working with data nominally encoded according to XML spec, yet unable to leverage any other parts of the standard XML ecosystem, then I would certainly agree that XML is a bad choice!
This. Once a document is big or complex enough, the benefit of human readability fades away and you are left with a brittle and very slow serialization format.
The problem is XML is _markup_ language and it was forced into data serialization -- hence endless mocks and people hating it.
There are people who actually use JSON for markup... the resulting schemas are usually very bad, and they disappear quickly without even gaining fraction.
When used properly -- XML for markup, JSON for serialization -- each format is pretty nice, and will probably last forever.
LOL, I am just going to leave this here, but to those that complain about JSON or XML or any future format X: It's always going to be shitty, messy and hard to read. You can only do so much to get rid of extraneous symbols but at the end of the day the format is hard to read because there is hierarchy and repetition. It seems like everyone is waiting for a format that will make reading 2000 configurations "easy". I'll have to say, good luck.
I have successfully implemented this exact project in the past. The project allowed for runtime logic modification however tracking states was built into the platform to make debugging simple. As with every other library or framework, JsonLogic is a great tool if you implement it responsibly.
Maybe it's a kind of having your cake and eating it too: enjoying the flexibility of passing code around and running it with eval, while not having the security, stability and runtime problems implied by eval.
There are limited scopes in which this can work but it seems to be more about the environment or code loader than about the programming language, per se: for example, plugin systems for build tools and servers. The plugin might be isolated by runtime features (like in the JVM) or by processes (as some web servers do, or as Postgres does) but it's not language features that tend to be useful.
Tensorflow models and CSS selectors are both examples of fairly useful plugins that still have limited enough semantics to be sandboxed effectively.
I agree that there's basically never a good use case for shipping logic from server to client or vice versa in a formal application setting. BUT. I could definitely see a scenario where something like this would be handy for farming distributed processing out to naive nodes or workers that don't / shouldn't have access to the core logic.
Maybe you can tell me if there's a better solution. I'm open to hearing any options because I'm at a loss of how to do this otherwise:
Say we have a schema which defines certain data. Let's say it's a "credential" which is a "COVID-19 Back to Work Credential." The credential is valid in the case that someone either 1) has a COVID vaccination or 2) has a negative COVID test in the past 72 hours. That data might look like this:
{
"credentialName": "COVID-19 Back to Work Credential",
"credentialIssuer": "Acme Labs Inc.",
"credentialData": {
"rapidSarsCov2PCRTest": {
"hasTestResult": true,
"testResult": "negative",
"testTime": 1622145851
},
"covid19Vaccination": {
"hasVaccination": false
}
}
}
Now, say I want to define the conditions under which this "credential" is valid or invalid, but I want to include this "matrix" within the schema (not any business logic, just matrix logic that pertains to the data within the schema itself).
Optimally, I'd be able to do this within the same schema or file.
With JsonLogic, that might look like this (probably flawed logic, just a demo, not using this in production, may cause your computer to go up in flames, etc.):
I'm not sure how to express how much I loathe everything about this "solution."
To answer your question, what you have is (effectively) code, and just use a programming language for this, call it a function, give it a name, arguments, test suite and register it as a plugin to your platform. Oh, and documentation, change history, etc.
Seriously - the "overhead" more than pays for itself when 100 other yahoos do this nonsense and you end up with data files containing untestable code that breaks when combined with other code, breaks when the platform is changed, etc etc
Ok, I take your point, but let's say we need to share these credential objects between different systems that do not have prior knowledge of how to evaluate the credential data. Also, let's say we constrain the logic to only allow for evaluation of any given part of the credential data against a binary "pass | fail" matrix.
What is a better way to share this important part of the schema (the matrix) without resorting to letting the "business logic" perform the evaluation? Optimally, any matrix should be able to run against the same business logic for evaluation.
Also, though I understand why you say that, we won't have "100 other yahoos doing this nonsense," and each schema would go through a pretty thorough review process.
> but let's say we need to share these credential objects between different systems that do not have prior knowledge of how to evaluate the credential data
Well, they _must_ have prior knowledge about it, because they must have to understand the concept of JsonLogic beforehand. So instead, you can just make these systems understand a proper programming language that can produce outputs. Something like https://github.com/dhall-lang/dhall-lang would probably be a much better option for this usecase.
With the standard that we're working with (the W3C's "Verifiable Credentials" standard [0]) having something that's in JSON or at least "JSON-compatible" is quite important.
If your business logic doesn't meet the needs of your business without the schema, the schema is a part of your business logic, that should be subjected to a suite of automated tests.
If your concern is the sharability, consider making a library or service that provides the business logic.
We'd like to be able to include evaluation matrices within credentials as an extension to the Verifiable Credentials standard (see https://www.w3.org/TR/vc-data-model/). Optimally, different libraries and different services will be able to evaluate a credential using the credential's included matrix (i.e. the evaluation matrix should not be reliant on any business logic from any particular party). I can't get too deep into the use-case but having these parts bundled together is important for what we're working on.
Sometimes you need to embed an expression interpreter in your application, for example to allow users to define them at runtime. In cases like that one, having a library with a well defined specification could save a lot of time.
It's much more likely that the library would do more harm than good.
Libraries tend to cover broader spectrum of use cases than you'd need. Usually that's okay (eg: URL parsing, string manipulation, HTTP client, etc) but in cases like this the logic schema should be as absolutely minimal as possible.
If you're building an interpreter and need an interchangeable format then you can always generate that out of your own bespoke and very constrained format.
With all due respect, you're making a classic mistake: presenting the solution to a problem without tracing the problem back to the use case.
Why do you need to include the logic matrix in the schema? I can promise you that the real world use cases where you'd need to this are virtually non-existent. You wouldn't even do this for some extreme case like Mars rover software.
Your business logic should have a single source of truth. The vast majority of the time you can just write an API for other software to synchronize with.
Otherwise just ship new builds of your software package to the client instead of a new schema. If you need to be modular then use modular primitives such as shared libraries.
Again, there's virtually no real world scenario where you need to ship a logic schema (in any format) to a client. You might as well ship a shared library, importable module, a completely new binary, etc, etc.
Interoperability is the main reason. We need this data and the evaluation matrix to be portable across different "credential wallets" as part of a standard model for matrix evaluation of Verifiable Credentials (as an extension that we're working on to the existing data model https://www.w3.org/TR/vc-data-model/).
You didn't state the use case initially. The context of the discussion is evaluating the value of the approach. What I'm saying is that you can't make that evaluation without stating the use case. Obviously one can think up a scenario where JsonLogic can be useful - that doesn't mean anything.
> Interoperability is the main reason. We need this data and the evaluation matrix to be portable across different "credential wallets" as part of a standard model for matrix evaluation of Verifiable Credentials (see also https://www.w3.org/TR/vc-data-model/).
Representing verification logic in the schema flawed. Just because the W3C publishes a spec doesn't make it good.
So I guess one of the few valid use cases for JsonLogic (or similar) is if your company makes a bad technical decision and you're forced to implement something?
> Representing verification logic in the schema flawed
The "verification" required is one of tamper proof, cryptographic safe certificate chain verification, something like the one done using Certificate Authorities, certificate revogation list and the surrounding infrastructure used in TLS.
Here is the thing...There is already a crappy way to describe JSON Schemas[1,2].
It is hard to grok by visual inspection and expressing conditions like "this element can either be a list containing a subset of these values or a hash with keys that belong to the same set" etc make it unwieldy.
The schema ends up being really brittle and hard to modify. 100% green-lit test coverage don't mean a thing when it is soooo easy to end up with a false negative.
Now take that and add logic to it? One has to question the existence of all the programming languages in use if we are going to end up subjecting ourselves to that torture.
Clojure Spec works really well for this sort of thing. It doesn't serialize to JSON, but it would be able to check for it.
You could also think about Dhall as a front-end. You could represent the whole credential as a union type. This isn't deeply thought out, but this should give an idea as to how this would look:
let TestResult = < Positive | Negative >
let ScreeningTestDetails =
{ hasTestResult : Bool, testResult : TestResult, testTime : Natural }
let CredentialData =
< Vaccinated | RapidSarsCov2PCRTest : ScreeningTestDetails >
let Credential =
{ credentialName : Text
, credentialIssuer : Text
, credentialData : CredentialData
}
let result
: Credential
= { credentialName = "COVID-19 Back to Work Credential"
, credentialIssuer = "Acme Labs Inc."
, credentialData = CredentialData.Vaccinated
}
let result2
: Credential
= { credentialName = "COVID-19 Back to Work Credential"
, credentialIssuer = "Acme Labs Inc."
, credentialData =
CredentialData.RapidSarsCov2PCRTest
{ hasTestResult = True
, testResult = TestResult.Negative
, testTime = 1622145851
}
}
let prop0 =
λ(x : ScreeningTestDetails) →
assert : greaterThan x.testTime 1522145851 ≡ True
in result2
Notice here that the type for credential data can only be vaccinated or a test result. It can't even represent unvaccinated. You could probably take it farther with dependent types and assertions.
Edit: added an assertion to model the 72 hour requirement.
I presume jsedn is your library [0]? I was heretofore unaware of EDN, but it looks like a very clean fit for our needs. I will examine this more in depth, thank you.
I'd love to do this, the only problem there is we're sharing this data between systems which use different runtimes. A client who runs a Python environment might not easily be able to make use of that logic. With JsonLogic, that would theoretically be possible.
Well JsonLogic is basically Lisp expressions in Json syntax which is kind of the worst of both worlds IMHO.
It seems weird to me to pass a syntax tree over the network rather than just the syntax. But if the expression is not supposed to be viewed or edited as code at all, but only in some kind of GUI expression builder, then it might make sense.
Lisp is at least human-writable, but if it is supposed to be written by humans why not just use straightforward expression syntax like "a + b".
I posted this in another thread, but it seems relevant to what you are looking for: https://stopa.io/post/265
It walks through the process of building a Lisp in JSON and the associated parser in JavaScript. It could be a good starting point to build out a cross-platform approach to sending logic over the wire.
A lot of shallow, smug snark coming into the comments section from people who know what s-expressions are and I guess want to brag about that for some reason. S expressions are cool but that's just _a syntax_, there are other possible syntaxes for expressions, and it's perfectly fine that somebody chooses to embed a language in JSON rather than s-expressions. Everyone suggesting to use Scheme or Clojure or Common Lisp and dropping the meme-quote that everyone eventually reimplements Lisp are missing the fact that this language is designed to be evaluated deterministically, so it does not support looping or functions and is Turing-Incomplete, unlike Clojure/Scheme/CommonLisp.
Ultimately it’s all data. The difference between this and lisp is just a bunch of extra characters and noise in the stream.
What is really the meaningful difference between this and an s-expression language? “” and {}, really. It’s a different way to serialize the same thing.
The difference is the collection of extremely powerful parsers, tooling, language support, etc that already exist for this syntax. There's a proper Content-Type header, blazing-fast native parsing in web apps, you could store it directly in MongoDB or the like without an extra serialization/parse step, you could query it with jq. Heck, you could statically-check these expressions using TypeScript.
Lispers are always pointing out how the parenthesis-and-whitespace notation is just incidental; how s-expressions are really something deeper that isn't bound to a specific syntax, and how this is a strength. The OP is a demonstration of that strength.
I remember years ago when i first came to hackernews there was a project very similar to this. An engineer blog proud of the AST they encoded as JSON.
I had only a vague idea of what an AST was, but the highest upvoted comment was clearly saying: This is dumb, we have dedicated formats for turning text into ASTs. ( i.e. a programming language ).
In the past decade+? i have learned what an AST is and have now twice, in a professional setting, prevented an engineer from re-inventing this very concept.
If i had to choose if hackernews is a place where smug snark comments crack down on re-inventing known concepts or a happy place where every idea is a good one i will choose the former.
JSON is a serialization format. So say you have an in-memory AST, and you want to serialize it: why not use JSON?
If you don't want or need to serialize it, then of course don't use JSON. I can make up plenty of other reasons not to, but they're variations on some other format being better for the application, not JSON being inadequate in any way. I'd probably use JSON myself, and I don't care for it much.
Or are we talking about building up an AST in JSON directly rather than taking some parser output and serializing it? Ok that's... not an AST then, it's a programming language with JSON compatible syntax, and yes I would try and prevent that from getting implemented as well.
Yes, we have programming languages. Lots of them. Most are distinguished by what you can do with them. Few are distinguished by what you can not do with them.
This is one of the few projects addressing the latter problem space.
>>What is really the meaningful difference between this and an s-expression language?
>Turing completeness.
S-expressions in and of themselves are obviously not Turing complete. This language could easily have been encoded in S-expressions, just like Lisp can be encoded as JSON. (Like, trivially, as lists with quoted strings instead of atoms, or using whatever object-based syntax.)
Correct me if I'm wrong, but not every s-expression language is Turing-complete, you need the ability to define functions which is not a given. This project doesn't have functions (or loops) seemingly on purpose (and their addition would make this weird language just as Turing-complete).
I think it's just a natural law that information tends to "crystallize" into certain patterns, and we're just the implementors of those repeating patterns. :)
I think comparison to s-expressions are a red herring. In Lisp, I guess, s-expressions are an integral part of how you code. As a piece of general code, it can be great or terrible. This "JsonLogic" thing is explicitly intended to be "logic-inside-configuration" because (I hope) nobody's writing program with JsonLogic. Unfortunately, logic inside configuration is almost always a bad idea. (It doesn't matter whether you're using json or xml or s-expressions.)
Don't most of the problems with "logic-inside-configuration" come from turing-completeness? This spec has neither control-flow nor reusable functions (recursion); it's nothing but simple expressions. That seems safe to me.
Here's the thing: I implemented exactly this for a side project where I needed users to be able to express simple logic for custom data handling rules. Here are some examples:
level = new Expression(['map', ['char', ['tail', 2], 0], {'=': 1, '-': 2}])
body = new Expression(['parse', ['trim', ['body']]])
name = new Expression(['replace', ['trim', ['body', 1]], /[ \t\r\n]+/, ' '])
target = new Expression(['first', ['body', 2], ['body', 3], ''])
The arg to `new Expression` is a Javascript object (i.e. can be sourced from JSON). The first element in each nested array is a function name/operator. It was absolutely trivial to implement. All the functions are implemented in a few lines of code within cases of a switch statement, and nested expressions are handled recursively.
For me this is just a temporary throwaway solution while I work on other more critical stuff first. I will replace it with an s-expression based DSL later. Like JSONLogic, it won't be Turing complete.
It just seems to me that JSONLogic is making a huge deal for what's really just a small reusable library.
They may be shallow comments, but they have a point. JSON is fundamentally unsuitable for anything numerical for a variety of reasons [0]. Thus, it actually can't ever even get to the point of being a suitable replacement for expressions in the languages you listed. Most web tech has insidious anti-intellectual designed baked in, and the end result is that people end up rolling their own mutually incompatible bespoke solutions to problems that were solved before the web was even a glimmer in the eyes of the advertising industry.
With s-expressions where that functionality is simply not provided as part of the underlying syntax at all, then every solution is a mutually incompatible bespoke solution. You could just use all string values in JSON and get the same effect.
This is a fun project and a cute reinvention of Lisp, as people have already mentioned. I think people are snarky because if this was just a fun project, that would be the end of it. But it seems like JsonLogic is trying to be an actual thing.
And this latter point is why it's just a monumentally terrible idea. I mean.. just look at the Custom Operations wiki[1]. It's honestly just horrible -- horrible -- and may actually even summon Zalgo[2]. This is exactly how we ended up with the XML nightmare of the late 90s/early 2000s. JSON is not meant to be programmable. Please, for the love of all that is holy, stop.
Yes! Oh my god, this. I remember how XML ended up being abused to express Turing complete logic in too many instances. Then they even specified an XML format to transform other XML documents, called XSLT, which is itself Turing complete. It really makes my head hurt.
I think why people gravitated away from XML was because it had become a convoluted mess. The JSON specification is terse on purpose, famously enough so to fit on a business card. If you want to abuse JSON to express logic, please keep it as your dirty little secret.
We built a rust version of this that provides a Python package via CFFI and a node package via WASM, because we found that the different implementations for different languages were pretty buggy and would sometimes resolve expressions differently. Also the original project in JS seems to be largely unmaintained. The rust version goes to great lengths to reimplement weird JS typecasting so as to be fully compatible with the original.
I am currently working on an extension to add functions and more strongly typed operators to avoid the hassle of JS’ type conversion. That extension will probably be released as a separate package, because I am no longer with Bestow.
Any sufficiently complicated program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. (Greenspun's tenth rule)
Are there any competing dialects of Lisp which have deterministic runtime? If people are reimplementing half of Common Lisp, then they are not implementing something that competes with this project because their language is probably turing complete. From the JsonLogic website:
> JsonLogic has no setters, no loops, no functions or gotos. One rule leads to one decision, with no side effects and deterministic computation time.
I have been programming for 40+ years and have never ever seen this in practice. And I am the kind of guy who have implemented my own Lisp more than once. Just for fun. However I would never use it in my day job.
At Routable we use this to power a significant portion of our dynamic UI (super complex forms with interwoven dependencies, etc). Super cool and useful library with a surprisingly low number of gotchas for what it's doing.
We did a little write-up about our experience with it here if anybody is interested:
Thank you, this kind of real-world experience and feedback is exactly what I was looking for when making this post! I will take a read through that blog :).
There are legitimate uses for such things. We are currently implementing a processing rules configurator UI and backend that must ultimately be executed in sql. It must support arbitrary nested boolean logic and comparison expressions. Last time we did this we ended up developing a custom data structure for the front-end and a custom printer for the sql output. This is a standardized version of solving just that problem.
My question is if not a solution like this how then SHOULD I be implementing communication of such rules from the frontend to the backend?
I think avoiding a parser by using JSON is kind of neat, but it really isn't that difficult to write a parser for a small expression language and you get the benefit that you can expose a much nicer syntax to end users with the same parser - rather than having an standard AST and need a parser anyway to have decent UX.
I work on and off on a very simple syntax language for things like templates, but would be well suited towards business rules and such too. It was the first parser I wrote outside of school and pretty easy to understand, small and fast too: https://www.npmjs.com/package/jexpr
Probably the biggest benefit of using something like JSON is the fact that you can safely and easily generate dynamic code without having to worry about different quirks of the particular grammar
I did a rules engine to the company that I work based in this specification, but using go language. It is a very successful project because our clients can create rules without tech people around in our frontend, it's very easy to translate our rules into json and store it.
This project reminds me quite a bit of [0] which I'm currently using to write rules to parse tags from Openstreetmap.
Having a standard way to share expressions does seem quite useful, especially when it's multilingual.
I don't understand why they represent it as a object with a single key, whose value is an array:
- If it's an object, then nothing is going to enforce there only being one key.
- If it's a unary operator, then the value doesn't need to be wrapped in an array, but then how does it distinguish between an unary operator, whose operand is an array, vs. an n-ary operator?
Both of these problems would be solved by having each clause be an array, with the first value being the operator, and the rest of the operands being the parameters. That way having multiple operators isn't even syntactically possible, and having multiple operands is just as simple as the unary operand case.
The idea of representing an expression as an abstract syntax tree for interchange is not an inherently bad idea. Trying to shoehorn a general purpose data interchange format into a highly specialized application like that is a common anti-pattern, however.
When I see this sort of thing (which I assume was inspired by MongoDB's own atrocious "query language") I'm always surprised by the fact that many engineers don't seem to be aware of how easy it is to write a parser that is actually designed for the data you're trying to represent. At least "easy" in the context of undertaking a project that is attempting to generalize this exact problem.
JSON is a fine interchange format until you need a specialized runtime to evaluate the data, which is what is happening here. At that point there's zero benefit to using a general purpose interchange format just because it already exists: just write a DSL.
man, I implemented a rules engine library where i expressed, the rules as S-Expressions (basically a lisp) that mapped to ruby lambdas. that way when finance would come back and be like, "what was the logic and the rules at a given point of time, I could point to what the rule was, what was it configuration, and who changed the configuration or the rule."
and the rule execution strategy.
It works, I'm proud of it, the lisp layer is completely abstracted away from the developers and the users who configur ed the rule.
I thought I was so clever :) Apparently, engineers have a habit of doing this.
What is the advantage of this being JSON? Couldn't the language be more "terse, consistent, secure, and flexible" if it wasn't JSON, with the possible advantage of parsing faster and being friendlier to write?
The website lists the ability to easily send it "between front-end and back-end code, and even store it in a database", is there an advantage to it being JSON for any of these goals?
We blindly use JSON for everything because one day, someone discovered that JavaScript embedded a JSON parser called eval(). Yup, that’s where our civilization is.
Thanks for the input. I submitted this because I’m looking around for ways to define a matrix for accepting a given “credential” within the schema of the credential itself — a bit hard to explain without getting into it. This is the best model I’ve found so far to do what I’m trying to do, but it’s still very imperfect.
We used this at an early version of ad-network where Advertisers would write JsonLogic against a set of user properties with all the operators. And this was checked at runtime when ad was to be shown. At the time, I thought this was a neat and quick way to give advertisers some form of targeting. We had ~500 active ad campaigns at any given time(not much growth beyond that) and this worked really great.
But I always thought there should be better way for this kind of application. I see a lot of comments about Lisp. My question is: Is Lisp better for above scenario or there is even better way?
This sort of rule language you can put in configs is often useful. They tend to eventually cause problems as logic that should have been implemented properly gets embedded in the mini language and the little rules get more and more complicated (or even auto-generated from a simpler specification), but they are invaluable while they do work.
One thing that surprised me was the inclusion of map/reduce/cat. Allowing these operators can make runtime exponential in the size of the program. I feel like that is against the spirit of the language.
But who cares? The fact that the syntax of this language is JSON-based rather than S-Expression based is an odd point to have the majority of commenters nitpicking about. When somebody posts an interesting new service to Hacker News, we don't have similar comments saying "why does this api serve up JSON and not S Expressions / Protobuf / Avro / CSV / XML???"
What about regular every day programming languages? When a language is posted to Hacker News we don't laugh them out of the room with "hahah! Why didn't you use S expression syntax for this language??"
We do something almost like this, but we didn't try to invent any of the hard bits. We serialize our domain instances into JSON documents which also happen to contain the business rules as in-line SQL properties, and then project this JSON document into SQL databases as required for evaluating business logic.
Ephemeral, in-memory instances of SQLite can be incredibly powerful tools for building an extensible business logic engine.
The combining of JSON & SQL is mostly inconsequential here. It is just a convenience for sending things in 1 file vs multiple.
The big takeaway is that SQL is a really great tool for exposing any sort of customizable logic over arbitrary datsets.
The only major caveat with this - You will typically have to perform some degree of transformation over source data in order to achieve a form of normalization well-suited to the writing of business logic (SQL). Assumptions (i.e. denormalizations beyond 3NF [0]) made in your schema here will inevitably hamper or kill the ability of the business to write flexible queries which can satisfy real-world needs. Today, a customer may have multiple accounts. In a few years, maybe we decide it goes the other way too. If you made an assumption, such as nesting these complex types in any way whatsoever, you are probably stuck with a steaming bag of shit that you now have to refactor and retest top-to-bottom. Using relation types to decouple nested complex types is the core tenant in my mind. Identity is foundation, but you can fudge your way around that a little bit.
This one caveat is why a lot of developers look at this idea as a bad one at face-value. It takes a lot of work & iterations with the business stakeholders to get this correct. You can't sit in a silo and expect to completely figure out the abstract nature of business types or learn how they might be related and in what ways. There really is a "correct" and "incorrect" way to go about it if you are being properly academic. Worry about webscale and performance later. Get it correct first. Nothing is too complex for a SQL schema.
If you can satisfy the academic gods of normalization and achieve a stable schema, then you are in for a wonderful ride. Between using views to compose higher-order functions, and UDFs [1] to wire into customized SQL functions, you can satisfy literally any degree of complexity required by the business. The only limit is your ability and discipline around modeling the domain types & relations, and willingness to get your hands dirty with some views and functions. You will find your business experts loving the power they gain by being able to write queries in a domain specific language they inherently understand. Giving them higher-order functions (views) and iterating on that side of the fence is yet another gigantic value play that is invisible if you are looking anywhere other than SQL.
Seriously. There are Lisp languages that could replace whatever language you're using, and, there are also Lisp languages/runtimes that you could embed in whatever language you're using, to avoid rewriting it from scratch like this.
Whichever works best for your particular constraints.
> It's like the difference between "use SQL" and "use SQLite" or "use Postgres."
Precisely! "Use Postgres" is a valid answer to the question, "which database should I use?", or "which relational database should I use?". And "use SQL" is a valid recommendation when you see someone reinventing relational data model and reimplementing a relational database without realizing it.
Similarly, "use a Lisp" is a valid recommendation when you see someone reinventing programming using trees from first principles - of which a telltale sign is reinventing s-expressions.
Probably not appropriate for a large poly-glot organization, but I really like creating shared JS libraries that can be used in the browser and also in Node.js apps. For game development, having the game logic in a shared library makes server-side validation and client-side prediction really easy and prevents code duplication.
I'm sure there is some reason it's all a horrible idea but almost all my projects these days use React front-ends and Node.js back-ends and a shared library used by both, ideally containing most of the complex logic. This seems like a neat version of that that might be useful in an organization which uses many different languages.
Seems like a really silly lisp with string operators and is incredibly hard to read when not trivial. This will probably be the first and last time I ever ask this question, but why not just use something like clojurescript?
I’m trying to define a matrix for the conditions under which a given “credential” can be accepted/is valid. The goal is to embed that matrix within the credential schema itself, such that it’s almost self-verifying, depending on the context of the credential’s usage/presentation. (A bit hard to explain without diagrams.)
Using JSON to do this would allow schema portability throughout the different languages in our stack, but of course it’s an imperfect solution. Open to hearing any other options (I have looked into YAML as well but that’s a whole other ball of wax)...
I might be misunderstanding, of course, but would you be able to use the JSON protobuf serializers and just define your conditions as Protocol Buffers?
S-expressions do all of this, which is why everyone is suggesting you've reinvented Lisp.
They're trivial to parse and easier to read than JSON for this use case.
But, if you really have an intense hatred for adding an additional parsing step on top of the JSON, you can still improve over JsonLogic with just JSON primitives used in a cleaner way that more directly represents that s-expression.
It's still uglier than s-expressions where you can tell symbols and strings apart with more ease, but without all the syntactical noise of JSON objects combined with arrays.
Thanks for the formatting, it's a lot better this way. I was using Prettier to make sure everything made sense as I a rather limited time to write the comment. Auto-formatting definitely comes with downsides...
I guess that last example you demonstrated works, but I fail to see an appreciable advantage of using that over JsonLogic. It seems quite similar aside from being "cleaner," and comes with the disadvantages of not being as well defined a spec as JsonLogic.
It reminds me of this [0] post from a few months back which implements something more or less similar using js arrays as a kind of sexp.
I’m working with a system that relies extensively on logic implemented as json structures. We’re not using this lib, I found it to be difficult to follow and more difficult to debug. Ymmv, but I’d definitely think hard before leaning on something like this.
I've been trying to invent a JSON-Lisp language like this for a long time. My first attempt was Jaspr (https://github.com/ar-nelson/jaspr), which has its own syntax that compiles to JSON. But I'm working on a new, Turing-incomplete variant called MiniJaspr that fills a similar niche to config languages like Jsonnet or Dhall.
I get it. I do ... it still rubs me the wrong way because:
1) You are leveraging a JSON parser to get strings
2) The strings contain tokens that need to be "parsed"
3) You turn this double-parse into an AST
I see this pattern all too often. If you create (or re-use an existing) grammar that is specific to the problem domain then you get a much faster parse, smaller data transfer/storage, and a more human-readable format.
The one thing that’s bugging me about this is the particular JSON structures employed. They’ve gone for this:
{"*operator*": [...args]}
With one special case:
{"*operator*": single_arg}
(This special case confirms that JsonLogic isn’t using arrays as a top-level construct, otherwise it’d be ambiguous. Well, it confirms either that or that it’s even worse-designed than it looks. Anyway, I’m going to assume arrays are free to use as a top-level construct.)
In short, you end up using an object and an array for each element. Yet the object is strictly being used as a two-tuple, and objects are a roundly bad idea for this application: the only way you’re ever going to be looking at it is “it’s an object, confirm that it has only one pair in it, then pull the first key out, and pull its value out”. This is quite inefficient in both memory and runtime performance. The only thing it really has going for it is that it guarantees that the key is a string, which is significantly overrated given the other more expensive bits of validation and access costs that it forces.
So the whole thing would be made more efficient (and arguably even more robust) by just changing it to an two-element array:
And then the natural next step is to say “why are we using two arrays rather than just one?” and flatten it into this:
["*operator*", ...args]
This is conveniently shorter to type, and eliminates the nasty single-argument special case too.
That’s very probably going to be more efficient on fixed-arity operators (which is most of them) and on potentially-unary operators, though it’s possible that it may be more expensive on larger calls if you need to slice the array during evaluation in order to exclude the operator.
So the end result I’ve suggested here is simpler, more consistent, smaller on the wire and in memory, faster, more robust across diverse languages and environments—in short, superior in pretty much every way. The only real place it might not be superior is auto-formatting, where you lose probable separation of arguments from operator (subjectively often desirable) but reduce the indentation level by one (more strongly desirable). It’s also more S-expressiony!
if you're wanting to reuse the same logic on both backend and frontend, you can just use JS on the frontend and backend and pull your functions/utilities in from a shared library - either a git submodule, private NPM package or (my preference) have all three running inside a yarn workspaces monorepo.
Like wasm, the idea of portable logic is interesting.
Unlike wasm, this is the wrong vehicle for it. And this implementation's very-limited nature means that anyone who buys into it will need to rip it out when the limited logic inevitably doesn't scale with the rest of their applications.
I worked at a marketing company and we had a targeting engine that was basically this, but not as elegant. Will give this a try this weekend and see how it compares in practice, cool project.
So, pretty much everything we've done in XML is now available in JSON. Wasn't the purpose of JSON supposed to be getting rid of XML's complexity?! Same with JavaScript - it was supposed to be a dynamic typing language, now most of the serious code migrated to the type safer TypeScript. We just keep going in circles and wasting so much energy! This is not progress; it literally is chasing our own tail!
I’d argue that in some cases it can be more testable than code. With a small, strict subset of a turing-complete language and constrained input choices, you can often auto generate test cases for any valid expression in a given context. Static analysis of code is much more difficult and broad.
One thing code versioning doesn’t do well either is executing multiple versions of the same logic. With rules as data, you can store them as timestamped versions and keep applying the correct historical rule against some older business object or view.
No this is not code - this is configuration. Many places actually save CODE into the DB and then load it at runtime for, say, ETL jobs. It's not an easy problem.
We are quite successfully using Starlark for an embeddable, executable config language. For example, I can take a Starlark expression at the command line and use it to filter elements of a collection.
> If you’re looking for a way to share logic between front-end and back-end code
So... exactly like isomorphic/universal JavaScript?
What exactly is the practical benefit in this, compared to say, JS functions shared by serialising them as strings or by abstracting them into a common dependency package which can be ran both on front-end and back-end?
Uhh.. json doesn't have comments. There is no code readability in here. If you are trying to represent code in data, night as well go use a language for that.
This is still a cool project. It issues a lot of effort to build something like this.
But I wouldn't use it for production until this has been around for a while and json gets comments.
I'm doing something similar in a side-project of mine as a temporary stop-gap to avoid writing (or generating) a parser for a proper s-expression syntax.
That seems worse than using the common C/Java/JavaScript syntax for operators and function calls, that can be easily parsed with any JavaScript parser.
The niche that immediately came to mind when I saw this was allowing a user to specify a filter expression for some query, either as a simple text-based language or using some sort of UI (with drop-downs and such). Having a well-established spec with existing libraries for turning these kinds of expressions into an AST for transfer, storage, execution later, etc is valuable. I've definitely implemented a simplified, domain-specific version of this kind of thing in various projects over the years.
It is a DSL for additional logic that can be:
- Stored in your existing data store (assuming your data store can store a string)
- Executed without eval() or any other potentially dangerous method
- Executed in bounded time
You think of things like Lua in Redis: this is one take on a not-quite-as-capable alternative for logic that can be safely executed in restricted environments (barring errors in the implementation of the parser).
There will be editing/verification requirements: writing any moderately complex logic structure without a designer/test evaluator would be a nightmare, but as a generic way to specify/embed custom logic in a system that is language independent, has effectively no additional parser requirements (given that just about every single language has a JSON parser that is probably already in use in your project) it isn't a completely bad look. S-expression parsers do exist, but I'd wager many more people are familiar with the JSON parser in their language than the S-expression parser available in their language.
I will say: it isn't complete. It lacks clarity about the behavior in the face of missing/invalid operation arguments. Something like
{"<" : ["ham", 42]}
I'd assume would throw an evaluation exception of some kind, but that should be clear in the spec.
The playground at https://jsonlogic.com/play.html is a start, but before you try handing this to a non-developer, you'd really want a visual editor with node folding of some kind (so you can hide deeply nested structures away while you work on other things), variable completion (so you specify a sample data object and if you're typing in a variable you get completion options) and operator parameter verification (to stop you from writing in incorrect/unsupported values based on the operator).
"Logic" is lazy word for "code" and we saw what happened when Excel "macros" grew up into major applications.
To be safe under maintenance, safe vs security, etc code needs at least these things:
- source control
- docs (and ideally, examples)
- tests (and ideally, CI)
Just to be clear — not my project, but I’m trying to use this or something like it. I found it pretty compelling though also somewhat lacking, and HN is a great place to get more discussion and maybe read about some alternatives :)
Sure, but then "truthy" for that language is just true, and "falsy" is just false. It's still relevant to understand the concept of "what is considered true, and what is considered false", even if the answer is simple for certain langauges.
Truthy is "what is considered true in a conditional". It’s still a meaningful question, with a meaningful answer, even in languages where the only thing truthy is the value true.
Basically it's very error prone. It's difficult to remember the rules which are usually quite complicated which leads to mistakes.
Much better just to be explicit.
> How do you even have a language without a definition of what is truthy and what is falsy?
You make it so that `if` only works on `bool` types, and don't implicitly convert other types to `bool`. For example if you want to check if string is non-empty, then you have to do `if (the_string != "") {` rather than `if (the_string) {`, which may cause your code to unexpectedly fail if e.g. the string is "false".
If you are reading this, and you think it is a good idea, please don't follow it as it is not.
Back in 2006-2009 I worked at MobiTV, which we had the same exact way/method of programing our mobile apps, on J2ME/feature phones. The made up language was called CEF which was based on XML. While the idea is sound in paper, This turns really fast into an ugly, debug everywhere type of scenario, where it is hard to both develop and debug properly.
The only way this is ok, is when you have some fully automated UI tool, that stores some kind of state, and retrieves it back, but with no human intervention and some very very limited logic. Otherwise you are in a world of pain. If you try to include real application logic into something like this it is going to hurt your business long term.
It will turn your business logic like some Maven configuration, full of gotchas and more.