Welcome to your first tutorial on phosphorus.five. In most of these tutorials, we will be using Phosphorus together with System42, which is a thin layer built on top of Phosphorus, that allows you to create, and execute, pf.lambda code, from inside of your browser. In this tutorial, we will learn how the pf.lambda [set] keyword works.
pf.lambda is a logic execution engine, which is at the core of Phosphorus. Notice how I did not refer to pf.lambda as a «programming language». This is because at its core, pf.lambda is not a programming language in fact. However, for most practical concerns, it is probably easier for you to understand, if you think of it as a programming language, at least in the beginning.
In pf.lambda, the notion of a variable does not exist. This is because everything is a variable in pf.lambda, and everything can be changed, including your «code». This is because in pf.lambda, you execute and modify the execution tree structure directly, and there is no interpretation process, or compilation process occurring during execution.
However, since it is impossible to modify the memory of your computer, without going through some sort of syntactic structure, Hyperlisp will be our preferred «language» of choice. This means that what we will be writing in most of these tutorials, is a «JSON lookalike» file format, called «Hyperlisp». Please realize though, that this is simply a format for creating and transmitting pf.lambda, and is not a programming language per se.
In the screenshot to the right, is the pf.lambda executor, which is a component in System42, that allows you to write Hyperlisp. The pf.lambda executor, will automatically change your Hyperlisp, to a pf.lambda execution tree structure, for then to have this pf.lambda tree structure executed, before the results of your execution is changed back to Hyperlisp, and displayed for you to see at the bottom of your page. This means that whatever Hyperlisp you write at the top of your pf.lambda executor, will be executed when you click the «execute» button. Then the results of that execution, will be shown at the bottom afterwards. This makes the pf.lambda executor work almost the same way as a «terminal window» on Linux or Windows. Let me show you an example to clarify this for you.
_foo:bar set:@/-/?value source:Howdy World
Above I have written some Hyperlisp, which will be converted into 3 pf.lambda nodes. The first node is a simple «do nothing» node, which is used as a variable in this example. The second node is a [set] statement, which is a keyword in pf.lambda, that allows you to change existing nodes, values and names, and the third node is a parameter to our [set] statement, defining our source for our [set] operation. Write in the same code in your own installation, and click «execute».
As you can see at the bottom, our [set] statement changed the value of our [_foo] node into «Hello World». This is because the expression in our [set] statement, refers to the [_foo] node’s «value». To understand how this happened, you must realize that pf.lambda has a «name part», a «value part», in addition to that each node can also have its own «children nodes».
The name is to the left of the colon, the value is to the right, and each child node is underneath the node it belongs to, with two spaces of indentation, declaring that it belongs to the children collection of the node above it. This is similar to how JSON works, except that JSON is a simple «key/value» format, while Hyperlisp and pf.lambda is a «key/value/children» format.
The above expression, found at line 2, basically communicates; «Use this node’s younger sibling’s value part». The minus sign tells the pf.lambda engine to use the «younger sibling», while the «?value» tells it to reference the value of that node, and you can see that it’s an expression, since it starts out with an «@» character.
An expression can de-reference zero or more nodes, which allows us to use one single [set] statement, to update more than one value. Consider the following code;
_data foo1:bar1 foo2:bar2 set:@/../*/_data/*/?value source:Hello World
Above we have 5 nodes. Our [set] statement above, will change the value of all children nodes underneath our [_data] node to «Hello World». Try it out for yourself in your own installation.
This is because the above expression basically says; «Give me the root node of the execution tree, then return all of its children. Then filter away all nodes who’s names are not ‘_data’. Then give me all those node’s children nodes, and de-reference their values». If we had multiple [_data] nodes at the root of our execution tree, our [set] statement would change all children of all of our [_data] nodes. Consider this code;
_data foo1:bar1 foo2:bar2 _data foo3:bar3 set:@/../*/_data/*/?value source:Hello World
Above we changed also our [foo3] node’s value, because the expression in our [set] statement now returns two [_data] nodes.
Each pf.lambda expression is made out of an «@» sign, zero or more «iterators», and a «type» declaration. The «@» sign is mandatory, and tells the pf.lambda engine that it’s an expression. Each iterator, reacts upon its previous iterator, to return some sort of result, according to the iterator to the left of itself. Each iterator starts with a forward slash. The «type declaration» tells the pf.lambda engine what type of result we are requesting from our expression. There are 5 basic types of expressions, «name», «value», «node», «count» and «path». If you change the above «?value» to become «?name» for instance, and execute your code again, you will update the «name» parts of your nodes, and not their «value» parts.
Try changing the ?value to ?name, and then click execute again. As you can see, the modified code changed the «name» of our nodes, and not their «values».
«node» allows you to change the node itself, while «count» and «path» are «read only» values, and hence cannot be used as destinations in our above [set] statement. To understand how a «node» type of expression works, consider the following code.
_data foo1:bar1 foo2:bar2 set:@/../*/_data/*/?node source:"foo-update:bar-update"
Above you see an example of how to modify the entire node, meaning both its «value» and «name» part at the same time. Notice how we had to use double quotes for our source above, since the «value» of our source node contained a colon. If you have a string containing either a colon, or a carriage return, then you need to put that entity inside of double quotes, like we did above. This is true both for «names» and «values».
Now it’s time for an exercise;
Change the code you currently have in your pf.lambda executor, such that the «foo1» value becomes the entire «foo2 node». Or with other words, make the «bar1» text above, become the entire «foo2» node. When you have done so, you will see a resulting pf.lambda code that contains a «type declaration» of your «foo1»’s «value». Hint; to do this, you must change your source above to also become an expression. When you have tried, and either succeeded, or given up, you can see an example below of how you can do this.
Thank you for reading, have a nice day 🙂
In case you didn’t understand how to solve the exercise in the video, the solution is given here;
_data foo1:bar1 foo2:bar2 set:@/../*/_data/*/foo1/?value source:@/../*/_data/*/foo2/?node