Readit News logoReadit News
ardit33 · 4 years ago
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.

jchw · 4 years ago
I am not saying that you are wrong. Not at all.

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.”

janderland · 4 years ago
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.

healsjnr1 · 4 years ago
I think the key point on this is `some use cases`.

For people who've spent a lot of time with ANT, you might be familiar with this article from its creator: https://web.archive.org/web/20041217023752///x180.net/Journa...

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.

valenterry · 4 years ago
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.
Too · 4 years ago
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.

smitty1e · 4 years ago
> 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?

enriquto · 4 years ago
> exactly this method in 2004-2006 but with XML.

Of course! JSON is the modern equivalent of XML. In 10 years everybody will mock JSON just like today we mock XML.

juped · 4 years ago
JSON mania has given me an appreciation for XML not having been so bad after all.
fastball · 4 years ago
I dunno, for me JSON will always be better than XML due to it just being nicer to read / work with.
theamk · 4 years ago
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.

Pxtl · 4 years ago
People mock JSON today. No comments, hideous multiline strings, and weak schema support.
rhacker · 4 years ago
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.
pydry · 4 years ago
It sounds like your language didn't steer clear of Turing completeness.

That's usually how this type of thing goes wrong. I've seen similar things both work and fail for that reason.

This language made it an explicit goal, though.

commandlinefan · 4 years ago
> If you are reading this, and you think it is a good idea

I came to the comment section here hoping that the site was actually a joke...

marktolson · 4 years ago
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.
solidsnack9000 · 4 years ago
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.

noduerme · 4 years ago
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.
sebmellen · 4 years ago
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.):

  {
    ...

    "parsingMatrix": {
      "if": [
        {
          "or": [
            {
              "===": [
                { "var": "credentialData.rapidSarsCov2PCRTest.testResult" },
                "negative"
              ]
            },
            {
              "===": [
                { "var": "credentialData.covid19Vaccination.hasVaccination" },
                true
              ]
            }
          ]
        },
        "valid",
        "invalid"
      ]
    }
  }
Can you think of another way to do this that doesn't rely on using JsonLogic or something like it (or a cleaner method for doing it in JSON)?

asah · 4 years ago
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

0xFACEFEED · 4 years ago
Straw man.

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.

nanis · 4 years ago
> With JsonLogic, that might look like this

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.

[1]: https://json-schema.org/ [2]: https://json-schema.org/draft/2020-12/json-schema-core.html

mmcdermott · 4 years ago
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.

rauhl · 4 years ago
> Can you think of another way to do this that doesn't rely on using JsonLogic or something like it (or a cleaner method for doing it in JSON)?

This strikes me as nice:

    (parsing-matrix
     (if (or (= (var "credentialData" "rapidSarsCov2PCRTest" "testResult") "negative")
             (= (var "credentialData" "covid19Vaccination" "hasVaccination") "negative"))
         "valid"
         "invalid"))

contravariant · 4 years ago
What about:

    ["if", ["or", ["===", ["var", "credentialData.rapidSarsCov2PCRTest.testResult"  ], "negative"],
                  ["===", ["var", "credentialData.covid19Vaccination.hasVaccination"],  true     ] ],
           "valid",
           "invalid"]

?

shaunxcode · 4 years ago
I cant help but imagine what it would like like in EDN:

    {... 
     parsingMatrix
     (if (or (=== (var "credentialData.rapidSarsCov2PCRTest.testresult")
                  "negative")
             (=== (var "credentialData.covid19Vaccination.hasVaccination")
                  true))
         "valid"
         "invalid")}

goto11 · 4 years ago
How about representing the logic as a JavaScript expression:

    (credentialData.rapidSarsCov2PCRTest.testResult==='negative')
      || credentialData.covid19Vaccination.hasVaccination) 
          ? 'valid' : 'invalid' 
Seem easier to read and maintain to me.

oreally · 4 years ago
Precisely. If you want to do logic, use a programming language, not a data format.
joshlemer · 4 years ago
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.
whalesalad · 4 years ago
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.

So I’d argue a lot of the criticism is valid.

We do, as an industry, tend to run in circles.

brundolf · 4 years ago
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.

athrowaway3z · 4 years ago
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.

And once every blue moon we get to laugh and look back at snarky comments such as this: https://news.ycombinator.com/item?id=9224

Deleted Comment

sebmellen · 4 years ago
The most important difference to me is that this is usable within already extant JSON Schemas that can be patched up or added to.
pydry · 4 years ago
>What is really the meaningful difference between this and an s-expression language?

Turing completeness.

>So I’d argue a lot of the criticism is valid.

I'd argue they should have paid more attention in college. This is far from a superficial difference.

You might as well call HTML a programming language. Seriously.

forgotmypw17 · 4 years ago
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. :)
yongjik · 4 years ago
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.)
brundolf · 4 years ago
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.
eevilspock · 4 years ago
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.

karmakaze · 4 years ago
I was all set to not like this. Without even getting into the depths of it I can tell I misjudged it from the name.

> JsonLogic has no setters, no loops, no functions or gotos. One rule leads to one decision, with no side effects and deterministic computation time.

It's like eBPF for applications!

The fact that it use JSON instead of your favorite serialization format isn't the thing to be talking about.

tgbugs · 4 years ago
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.

0. https://stackoverflow.com/questions/1423081/json-left-out-in...

shawnz · 4 years ago
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.
ironmagma · 4 years ago
> Most web tech has insidious anti-intellectual designed baked in

Citation? I don’t know many people who would call Brendan Eich anti-intellectual for instance.

dvt · 4 years ago
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.

[1] https://github.com/jwadhams/json-logic-js/wiki/Custom-Operat...

[2] https://stackoverflow.com/a/1732454/243613

vesinisa · 4 years ago
> 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.

nitwit005 · 4 years ago
The moderator's note on #2 adds a lot to the amusement.
mplanchard · 4 years ago
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.

https://github.com/Bestowinc/json-logic-rs

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.

goodpoint · 4 years ago
> rust version of this that provides a Python package via CFFI and a node package via WASM

I honestly thought it was parody around the unnecessary complexity of modern software.

sebmellen · 4 years ago
Wow, awesome. Thank you! Just the kind of thing I’m looking for :)
ktpsns · 4 years ago
And this is how LISP is invented.

Any sufficiently complicated program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. (Greenspun's tenth rule)

joshlemer · 4 years ago
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.

pydry · 4 years ago
It's Turing incomplete. Like HTML.

LISP is, on the other hand, a programming language. It's rather famous for it.

adamkl · 4 years ago
Here’s some great instructions on how to use JSON to do just that:

https://stopa.io/post/265

mbrodersen · 4 years ago
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.

Deleted Comment

sheminusminus · 4 years ago
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:

https://blog.routable.com/tablematic-a-tale-of-server-driven...

sebmellen · 4 years ago
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 :).
rendall · 4 years ago
For some reason, this project seems to be drawing rather nasty comments. I'd like to remind everyone of the guidelines:

Be kind. Don't be snarky...

Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something.

commandlinefan · 4 years ago
"This is a bad idea" is neither nasty nor snarky.
rendall · 4 years ago
Non-sequitur. While true, it has zero to do with what I wrote.
kvnhn · 4 years ago
It is, however, a shallow dismissal.
lootsauce · 4 years ago
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?