This is an effort to give a perspective to see config(data) as a code, which is not typical for programmers from an imperative programming background. With this you‘ll be able to gain a technique with which you can express any arbitrary logic as part of your config(data). In other words, where others see just data you can see code. For the benefit of a larger audience, We’ll explain this with an example and try to be as concrete as possible.
A while back, I was working on the problem of implementing a survey module for one of our clients. This example is about a subset of the bigger problem that any typical Survey module has to solve, the problem of conditional display of questions.
Let’s get straight into the problem.
Assume that we are building a survey web-app. Ans assume that we have to implement conditional branching. i.e., certain questions need to be displayed only when certain conditions are met (For ex, display/hide a question based on the answer given by the user for another question). By de facto, consider that we are getting the the questions and answer as a JSON and the UI is deciding which questions to show/hide. Let’s see how we can achieve the conditional branching here.
On a brainstorming with developers from Java background we understood that, If you are from a background of imperative language your solution for conditional display could most likely looks like this,
Where-in, in your code you give the meaning to the json by defining, By default all questions are displayed until “display” is mentioned as hidden. When a particular answer is selected the question mentioned in “displayOnSelect” will be displayed.
If you think about alternative choice write it down and see how it evolves as the below complexities are added.
Let’s add few more scenarios to the problem,
You’ll be required to invent a syntax to express the logic and give meaning to this syntax by interpreting it in a certain way as part of your code if you follow the aforementioned approach.
It is not easily extensible when additional constraints are added, we treat the json just as a data and the whole logic of display is just implemented in the code.
Instead, think if we can have an expression for each field that says whether to display that field or not. And think if the syntax of the expression is extensible to express any logic. And all we have to do in the program is to evaluate that expression and decide.
So do we have to embed a program inside the data and evaluate it as needed? If so, what would be the syntax of the language and how would we evaluate it?
So how about we come up with a custom syntax for the language that we’re going to use to define the logic of which questions are available? Yeah, we can. And with that we also need to make sure that the bringing in custom language doesn’t pull in a lot of other complexities such as dealing with parse, execution environment, etc.
So the simplest approach that we came up with is,
Let’s see how we can achieve our goal of conditionally displaying a question when the conditions gets complicated. For ex, your data for Q3 with the logic “Display Q3 when the selected answers are Q1A1 and Q2A1.” can like this,
So what we have done here is, we have expressed the aforementioned condition within our data(json), using s-expression, with the custom operators that are needed.
and are the operators.
And the meaning of the expression and the sub-expression would be as in the below table,
To evaluate the expression , you can use the library nisp and extend it with the implementation of required operators (ex, “and”, “equals”, “answer”). (You can even implement your own quickly. We did a Java variant in just a couple of hours). , which lets us evaluate s-expressions.
For ex, to evaluate the aforementioned expression, your code might looks like this,
answers is the global that you will be required to maintain, containing an array of answers given by the user.
In this way, you can evaluate expressions to express any logic, and it’s completely a sandboxed evaluation.