Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

One of the important features of programming with picos is creating children. Solving programming problems with picos usually involves creating a system of picos that cooperate to solve the problem. This lesson introduces the concept concepts of pico-based systems and pico life-cycle management.

The pico that is created when your pico-engine started for the first time can be thought of as is called the "root" pico. 

  • Every pico except the root pico has a parent. 

  • You can create as many child picos as you like. 

  • Picos can be the children of other child picos and so on. 

...

Here is a screenshot just before clicking the button:

...

And here us the screen after the button is clicked. Notice the new child pico has a (randomly assigned) event channel identifier (ECI).

...

Click on the "Close" button in the upper-right corner to see the layout of the pico collection so far. It should look something like this, after you reload the page move the picos around a bit:

...

For practice, create another child pico , starting at a pico of your choice in one of the picos and then delete it. Currently, the UI does not allow you to delete a pico which has children (until you first delete all of its children, etc. recursively).

Warning

Deleting a pico cannot be automatically un-done. Any channels and , installed rulesets, and state will be deleted with the child and can only be manually recovered by recreating it from scratch.

Creating Children Programmatically Using Wrangler

...

The "Root Pico" is created when a pico-engine starts for the first time. We have already created two of these picos, the Registration and Section Collection picos manually.

However, the two other kinds of picos (for individual sections and students) will be created programmatically, as needed. The diagram shows only a few of each, but in practice there could be thousands of each kind. BYU, for example, teaches over 8000 sections per semester.

Our next step here will be to write a KRL ruleset for the "Section Collection Pico". The purpose of this ruleset will be to manage its children, and serve as a sort of directory for them.

This pico will expect events from other picos or from the outside world (a web pageclass management system, for example) informing it that a pico is needed for a particular section. When the rule evaluates, it will create the section pico and see that it is properly initialized, and will then record the ECI of the new section pico for future use. We'll complete the process of connecting the section pico to student picos which need it in the next lesson on subscriptions. For example, student A might need a direction communication channel with section CS462-1, and this (pair of) channels is called a "subscription".

Create a new ruleset named, say, "app_section_collection" , that includes this rule:

Code Block
  rule section_needed {
    select when section needed
    pre {
      section_id = event:attr("section_id")
      exists = ent:sections && ent:sections >< section_id
    }
    if exists then
      send_directive("section_ready", {"section_id":section_id})
    notfired {
      ent:sections := ent:sections.defaultsTo([]).union([section_id])
      raise wrangler event "new_child_request"
        attributes { "name": nameFromID(section_id), "backgroundColor": "#ff69b4" }
    }
  }

...

Line 1 names the rule, in this case "section_needed"

Line 2 allows the pico-engine to select which events will cause this rule to be evaluated: those with domain "section" and name "needed"

Lines 3-6 constitute the rule's " prelude " which binds local* names to values

Line 4 binds the local name "section_id" to the event attribute named "section_id" for convenience.

Line 5 binds the local name "exists" to the result of the Boolean expression. The operator "><" is the membership** operator

Lines 7-8 constitute the rule's " condition/action " block and sends a directive if "exists" is bound to "true"

Lines 9-13 constitute the rule's " postlude". In a postlude, we can mutate entity variables and raise events

...

Line 10 assigns a new value to the entity variable , "union-ing" in the new "id"by taking the union of the sections variable and new id. If the entity variable has not yet been assigned a value (i.e. it is null), it will start out as an empty array.

Lines 11-12 raises an event, with domain "wrangler" and type "child_creation". This event is expected by the ruleset named "io.picolabs.wrangler" using the same rule that runs when you create a child pico manually.

* local in the sense of being lexically (and temporally) scoped to this rule (execution).

** the membership operator is described in the page Predicate Expressions

*** when the rule's condition evaluates to true, we say the rule has "fired". , otherwise, when the condition evaluates to false, the rule has "not fired"

Notice that the rule uses a function named "nameFromID" which is simply defined, in the " global " block of the ruleset, as

Code Block
    nameFromID = function(section_id) {
      "Section " + section_id + " Pico"
    }

Be sure the ruleset "shares" it in the " meta " block. With this in place, once you have validated and installed your ruleset, you'll be able to use the "Testing" tab of the "Section Collection Pico" and have a UI for adding section picos by sending events to your pico, which will be handled by your ruleset.

To install your ruleset in the section collection pico, visit its "Rulesets" tab, enter the URL to its KRL source code, and click the " Install " button. Next create a channel suitable for carrying with a policy that allows events with domain "section" and queries for this ruleset. Then you can visit the Testing tab and begin creating section picos.

...

However, this is taken care of for us by the design of KRL. When an event triggers a rule, that rule will run to completion, and no other event for the pico will be considered until it has finished. Not only does a pico encapsulate its state, it also synchronizes demands on it. In this case, all of the actions in the code block shown above will complete as an atomic operation. Only then will the pico consider the next event on its event queue. We describe this by saying that picos are “single-threaded” even though the engine is concurrent (i.e. multiple picos can be simultaneously considering events at any given time).

Listing the Children

We can use the Wrangler function children() to get a list of the children. First, we'll need to declare our intention to use the io.picolabs.wrangler ruleset as "using the alias wrangler". This is done by adding this line to the meta block:

...

As we do this, let's change our representation from a simple list (an Array) of section identifiers. Instead, let's use an object a map containing JSON objects named by the section identifier. To be concrete, instead of ent:sections having a simple array of string section identifiers like

...

Did you have a rule in your app_section_collection ruleset to empty out the list of sections? If not, create one, or modify yours so that it treats the empty collection as an empty object, instead of an empty array.

...

Wrangler will then create the new pico, and record the parent-child relationship between the "Section Collection Pico" and its new child pico. Then, wrangler Wrangler is written to raise an event using the code shown here. Note that the code shown below already exists in the wrangler ruleset. Do not add it into your ruleset.

...

with domain wrangler and name new_child_created

...

that has the following attributes.

Code Block
{
  // attributes provided by wrangler
  "eci": <new child's family eci>,
  // attributes you provided in the child_creation request, passed through
  "name": <the original provided name YOU gave>,
  "backgroundColor": <your provided color string>,
  "section_id": <section_id you provided>
}
*/
Info

Info

For more information on the wrangler operating system and different events you can listen in on and what additional attributes you can provide, check out the wrangler documentation. (This documentation has not yet been written. See this repo for work in progress.)You can also view the Wrangler repo to see the code.

You can add a rule in your app_section_collection ruleset to react to the wrangler new_child_created event. Your code might look something like this.

...

Having made these changes, you can go into the UI and in the "Testing" tab of the "Section Collection Pico" clear the sections, and in the "About" tab manually delete any picos you had created previously. You should also trigger the "section : needs_initialization" event to re-initialize the ent:sections entity variable.

...

Installing a ruleset in a child pico

Wrangler is prepared written to respond to an event, wrangler : install_ruleset_request, which can be used to install a ruleset in a pico.

Why would we want to install other rulesets into each section pico? Rulesets represent the desired behavior in response to events that we would expect from that pico. Picos are can be digital twins of real world things/objects, meaning they digitally represent some physical thing (in this case they represent sections and students). So, in simpler terms, if we want a Section Pico to accurately represent model a real section, we install the "app_section" ruleset into it that provides all the desired functionality. The same could be done for the Student Picos, but perhaps with a ruleset maybe called "app_student".

Make an empty ruleset named "app_section" (you could name it any reasonable thing you like). LaterNext, you we will install it in section picos, first manually, and then programmatically.

...

You can install a ruleset in a pico manually, using the "Rulesets" tab of a pico. Install your new ruleset manually in each of the section picos you created earlier. Of course, this ruleset would have additional functionality and you would want to keep it in your repositorythe section picos you created earlier.

Programmatically

Now, let's add code to our app_section_collection ruleset to instruct our new section pico to install its ruleset programmatically. Sometime after each section Pico is created, and before we need to send it the first event specific to sections, we would send this event to install the ruleset in it. This is an "action" which will be placed in the action part of the rule selected by the  wrangler : new_child_created event (replacing the noop()).

Code Block
      event:send(
        { "eci": the_section.get("eci"), 
          "eid": "install-ruleset", // can be anything, used for correlation
          "domain": "wrangler", "type": "install_ruleset_request",
          "attrs": {
            "absoluteURL": meta:rulesetURI,
            "rid": "app_section",
            "config": {},
            "section_id": section_id
          }
        }
      )

See the P.P.P.S. below for some discussion about the absolute URL.

The event is sent to the child pico, and is handled by the ruleset running Wrangler (as implemented by the ruleset "io.picolabs.wrangler", which is automatically installed in every pico which wrangler creates).

As it becomes clear which events must be handled by section picos, corresponding rules can be added to the "app_section" ruleset.

Now, create a couple of section picos (using the "section:needed" button in the "Testing" tab), and verify that each of them has the app_section ruleset (refresh the "Rulesets" tab of the new section pico).

After wrangler has installed the ruleset in the child pico, it will raise the event "wrangler : ruleset_installed" with the appropriate rid attribute. You could create a rule in your app_section ruleset to respond to this event, by storing the section identifier in an entity variable. We will leave this change as an exercise (a simple possible answer is given below in the P.P.S.). 

...

When you install a ruleset into a pico, you may wish to respond to the event "wrangler : ruleset_installed" by automatically creating a child pico. Your ruleset may wish to respond to the event "wrangler : new_child_created" by installing an appropriate ruleset into the new child pico. Finally, that ruleset might respond to the event "wrangler : ruleset_installed" by sending an event to its parent. 

...

When our app_section_collection pico is notified that a section pico has gone off-line, we can delete it. We'll see in subsequent lessons just how (and why and by whom) this event might be sent. For now, describe the event in the __testing structure and send the event manually from the section collection pico's "Testing" tab.

This could be done by a rule like this one:

...

We don't need to specify a complete URL for page2.html, because it is understood that the browser will resolve it to its complete value, "https://b1conrad.github.io/krl-sample/page2.html", based on the URL from which page1 was originally loaded. In a browser, the base is implicitly understood (and is available to JavaScript as "location.href"). With Wrangler, we pass the base, which is available to KRL as "meta:rulesetURI", as an attribute for the wrangler : install_ruleset_request event.