Let's start with a general overview of the way Elody works in the background.
The basic idea
Normally when you write a program, you need to write one single application that does everything you need. You may not have to write everything yourself, since you can import libraries for those problems that other people have already solved, but you do still have to find those libraries and integrate them into your own code.
The more features you add to your program in this way, the more technical debt you accrue: Each additional dependency makes your code harder to maintain.
You have to ensure that all of your dependencies are always up to date, and each update brings the risk of crashing a seemingly unrelated component.
Elody does away with all of that.
When you run your code with Elody, you can declare subtasks and delegate them to Elody.
Elody finds a way to solve the subtask using all of the other software at its disposal, then makes the solution available to your program.
That means you don't have to search for or install a dependency, and you don't have to maintain it either. Even better: If someone uploads a better way to solve your subtask, Elody will automatically switch to the better variant, and you won't have to change your code at all. You might not even notice anything, except that your program suddenly works better than it did before.
Elody acts as the central control unit for solving problems. Whenever you don't want to do something yourself, you can delegate the task back to Elody and see if Elody can use someone else's code to solve your problem.
Of course, you can still import libraries and use dependencies as normal, if you want to. Similarly, you can also instruct Elody not to switch to better solutions automatically, so you can examine them yourselves. But the option is always there.
We believe that in a few decades, this way of connecting programs will have become the new norm.
Step by Step
Let's go through an example problem step by step, and see how Elody solves it.
Suppose that our task is to make a timeseries prediction. The task is to get timeseries data from the user, perform a prediction on it, and present the results. We will actually implement this example later in the tutorial, but let's discuss only the generalities for now.
Let's also say that we actually only know how to perform a prediction on a timeseries, but don't know how to get a timeseries from the user or how to present the results. If this were a normal program, we would have to import libraries for some basic I/O operations, but Elody offers a much better alternative, as we will soon see.
First of all, we need a way to make our timeseries prediction available to endusers. The easiest way to do this is to define a Scenario Plan.
A Scenario Plan defines a few things:
- What is the initial task we want to solve?
- Are there any special parameters we should consider?
- Should any Rules get special treatment by Elody, like for example trusting a newly defined Rule even though it hasn't received a high rating, yet?
In the simplest case, a scenario plan lists a single Symbol that will be created when the Scenario starts.
This Symbol has only one purpose: Its presence triggers a basic Rule that defines our task.
This Rule goes as follows:
- Give Elody a new task: We need a timeseries file from the user. This task definition can also include some formatting specifications.
- Wait until Elody has solved the task and obtained a timeseries file.
- Run our own program to make the prediction.
- Take the results of the prediction, and give Elody another task: Present the prediction to the user.
Note that the tasks we define here are formally defined objects with a well-documented meaning, but that they don't actually have any code directly associated with them. Instead, developers can define Rules that react to the task objects, and those Rules define the logic used to process the task.
Whenever Elody receives a task, it checks all of the Rules that other developers have uploaded, to see if any of them are able to help. It picks the best rated one out of all the applicable ones, and executes it.
This way, Elody can react context-sensitively while also ensuring quality. There may be one Rule by one developer that can acquire a timeseries if a file has been uploaded, another Rule by another developer that can extract a timeseries from a SQL database, and a third one that just gives the user some options for providing data and talks them through the process in case anything is unclear. There may also be any number of competing Rules that just aren't very good and have poor ratings, so Elody ignores them in favor of the better ones. It is also possible to write Rules just to cover rare special cases of a more general problem: Elody will use your program if that special case occurs, and will otherwise resort to a more generic program.
Note that these Rules can even create further tasks recursively. For example, if a timeseries can be extracted from a file, but the user uploaded an Excel file and our program works with CSV files, then a subtask to convert the file to CSV could be created.
Elody can handle any number of recursive task definitions, and even complex interactions between tasks. It can also ask the user for clarification, or offer them a choice between multiple alternatives where appropriate.
None of this needs to concern us for our example program. We can just delegate the task "Get a timeseries from the user" to Elody, and wait for a result.
You can see how much easier this is than the ordinary way to do it: We do not need to search for a library that can parse an Excel file, and we do not need to import and maintain that library. We do not need to consider all the alternative ways to get a timeseries, like a SQL database. We just declare the task, and let other people solve it for us, through Elody.
Not only do we not have to keep track of a dependency, but if someone else uploads a program that can solve any of our subtasks more effectively, then our program will also get more powerful automatically. If someone uploads a program that can load a timeseries from Google Analytics, for example, this can also be integrated automatically and will work in parallel with the existing Rules.
All we need to make sure is that we use the tasks in the way they are supposed to be used according to their documentation.
We can focus our own code on the actual meat of the problem: Making the timeseries prediction.
To make that prediction, we can just write our code as normal and put it in a Docker Image. Docker works for pretty much any programming language, and we provide a library to make it very easy to communicate with Elody through Docker. More info on this can be found here, but the tutorial should make this clearer through examples on the next few pages.
Note on flexibility vs predictability
Elody will normally always use whatever Rules have the highest ratings in any given situation. That makes it very flexible and adaptable, but also means that Elody's behavior is constantly changing as new rules get uploaded and rated. This unpredictability is sometimes not desired, especially when you want to test something. You can use Scenario Plans to deal with this, by altering Elody's decision process. Read the next few pages for some examples.
Note on charging money
Later on, developers will be able to charge money for the use of their programs. This makes it possible to charge money for programs that would be useless on their own, and impossible to sell without Elody. For example, a program that extracts a timeseries from a CSV file is far too small and specific to be worth money on its own. But as part of an automated process realized through Elody, it becomes possible to make money with it.