The goal of Fosforescent is to increase the speed, scale, and ease at which groups of people can make decisions and collectively manage pooled resources and common goods.
In order to collectively manage pooled resources and common goods, ultimately people need to do things on behalf of the group that has pooled its resources. This applies to any group: a club, a corporation, etc. These are all special cases of the concept of a group of people who pool resources (effort, funds, equipment, etc.) to achieve a goal that is arrived at by some governance structure (voting, single person making decisions, etc.), and then distribute any results to the members. When the governance structure is more democratic and the resulting revenues are distributed among members, one might call it a co-op. When the governance structure involves automated democratic aspects and is managed through a blockchain, one would tend call it a DAO. These are also special cases of an organization.
So why is it a programming language? We need to look at what a policy is and what a programming language is. Ultimately a programming language is just a way of organizing a set of instructions that sequentially modify the state of the computer. Specifically they organize dependencies among states by preparing a certain state before executing a certain instruction. A policy is similarly a set of instructions. Those instructions have dependent instructions as well. Sometimes they’re explicitly organized in the form of a set of operating procedures. Sometimes they’re implicitly organized in the form of training. Either way, when an agent is assigned a certain task, it consists of an instruction as well as a certain context in which the instruction occurs.
Those instructions also generally contains requirements for acceptance, explicitly or implicitly. For instance, if the policy at some administrative office is that visitors must fill out a certain form upon arrival, then the form must be handed to the person working the desk. Until then, the process is waiting for the assignee of the form-filling task to fulfill their function. Programming languages often have these requirement and have established many ways of dealing with them in such a way as to reduce complexity, for instance type systems and constraint based programming.
Policy as Code
In order to scale group decision-making Fosforescent aims to create a language that can express the policies of any organization. Policies come in many forms. Some prohibit certain behaviors, others allocate funding or resources for certain behaviors. Others assign tasks to certain people under certain circumstances. A policy is also just another operating procedure. In a company, the operating procedure covers the chain of events that is triggered by, say, a work order arriving. Or a refund being required. A club might have a set of policies that cause tasks to be triggered when a member signs up. These sorts of procedures are often expressed as workflows. If you can encode policy, you can encode all of an organization’s business logic.
The following capabilities must be expressible:
Roles - This is essentially a variable of type “agent”. The variable can be reassigned, however they must be able to execute some tasks.
Procedures - These are abstracted groups of tasks which, rather than being assigned to individuals tend to be assigned to roles.
Tasks - These are steps that people are required to do to carry out procedures, where the role has been bound to the actual person currently fulfilling that role, so that the person fulfilling the task may receive some incentive.
Permissions - These are policies which limit the procedures that certain roles can execute.
Resources - This includes anything required to execute a procedure. In order to execute a procedure, some resources must be allocated. Some procedures also anticipate the acquisition of more resources, in which case upon execution of the procedure, more resources are added to the parent context, allowing other procedures to be triggered with those resources.
The “procedures” here are basically just workflows. Here’s another post describing some of the technical details of how these workflows can be expressed as functions, creating a dataflow language runtime which can host one or more programming languages. This essay will focus on defining the desired programming language features of a specific visual programming language which could be implemented on that runtime. This visual language which would serve as the interface for Fosforescent.
Overview:
First, here’s just a general description of how the language would work. The runtime exists as a single graph. Being evaluated by the interpreter versus having human input are special cases of the same thing: graph rewrites by agents.
Each node in the graph represents a list of expressions. Some of these expressions are data, others can be evaluated, others require agent input, for instance a confirmation or input from a human. Expressions can represent normal programming language expressions, but they can also represent essentially “todo” items. So they can represent high-level goals, like “learn guitar”, or fine-grained tasks like “add egg to mixture” in a recipe. Those tasks can be evaluated by an agent if it is capable of interpreting the instruction defined.
What this looks like visually is that there’s a view which is just a list of tasks requiring input from a user. Automated agents would similarly be presented with a list of tasks which are available for them to evaluate, but they would tend to be presented without the visual interface. The ones which can be evaluated are asynchronously evaluated by agents if they have the capacity and resources.
Basic Features:
Resource Allocations:
In a user’s outermost context, they can specify available resources. Some of these might be custom resources, data like “contacts”, documents, etc. Other resources might represent real-world items. For instance someone running an Etsy store might use it to represent inventory or supplies. It would also contain an available budget synchronized via API to something like Stripe, or eventually using with a blockchain wallet.
When running a task, the allocation may be provided to the workflow lazily or eagerly. An eager allocation would indicate that a lock be immediately acquired on the required resource. This would ensure that the task can run through without waiting for that resource. A lazy allocation would acquire the lock once the sub-expression which uses it is available for evaluation.
As a task is evaluated, some of those resources may not be fully consumed. Alternatively new resources may be acquired. These would be made available in the context when the task finishes evaluating. For instance a task which involves payment would increase the budget.
This would allow projection of supply and budget constraints as each task under evaluation could be searched for expected resources.
A group would also make use of this resource locking as a critical basic function for managing shared resources. Things like a shared group budget would allow users to propose and vote on actions like making a group purchase, as well as specifying how any new resources would be redistributed.
Pattern matching & Type System:
The easiest way to implement a flexible enough control flow is to make pattern matching the basis. An “if” statement would be resolved via a pattern match on a boolean input. There would also be cases where users or groups make a choice and a branch is chosen that way.
Sum types provide critical behavior for tasks—the ability to choose among a set of alternatives. Choosing “get milk” or “get water” as potential alternatives for the “get drink” task is a critical part of the basic task semantics. Each block would essentially be a product of each of the expressions it contained.
Type checking would ensure that, for instance, a task does not start getting evaluated if it has not been allocated the required resources. Since these are value-level specifications, dependent types would likely be required. Further, to properly express policies, we may have to go further and create requirements about the expressions within function bodies. This is because policies often stipulate things like “no policy can be created that has X, Y or Z characteristic”.
Group-based Actor system:
The actor system would be based around groups. Users would be a special case of group, as would the group that includes all users. These would exist as nodes in the graph as well.
These groups would also be agents, meaning they could register handlers for certain expressions in groups which they are a member of. This would in turn mean that other members of the group could create workflows which directly call those expressions. Those expressions could certain requirements, e.g. a payment. This would provide the basis for a task market.
Within the group, procedures could be used to define consensus mechanism. A basic mechanism would be provided which would look something like a pull request system. A user would make a proposed change to the state of the group graph. If enough other users approved the proposal it would become the new state.
Trade-offs:
A big trade-off will likely be speed. If programming language evaluation performance is required, users should be able to call out to external agents for that purpose. Since the expression evaluation is meant to occur alongside long-running asynchronous tasks, this is a trade-off that shouldn’t affect the intended use cases too much. Furthermore, allowing the implementation of other semantics may allow some mitigation of this issue if they are more optimal (e.g. interaction nets)
Another trade-off will be space. The content-addressed graph will take up space especially as past versioned nodes accumulate. Luckily space isn’t that expensive. This should also be mitigated by the fact that it is primed to be distributed via a Distributed Hash Table (DHT), meaning unused nodes can be pruned. Eventually a goal is for some setup where seeders are provided some benefit as well.
Another trade-off will be that it has primarily functional semantics, and on top of that the novel visual format. So it will have a learning curve. However the power that this could open up would be well worth the investment. Not only that, but the visual format opens up a lot of opportunity for creating novel intuitive syntax. This will be a process, but bringing value to the table by developing the visual syntax will be a core focus with the aim to provide a persistent advantage. Long term the goal is to open up other mediums as well such as gesture and voice which could further mitigate this trade-off.
Conclusion:
Fosforescent aims to expand the automation that people can orchestrate in their everyday lives, as well as create a new way of collaborating. In order to do that, it aims to use the latest programming language technologies to solve some fundamental requirements, as well as to manage the complexity presented to the user.
Programmers have developed tools to manage what is implicitly the same problem of collaboration that governance requires. By making them available in a format that doesn’t require programming expertise, we can harness those technologies to make decentralization an everyday reality.