The overall signaling model looks like this:
Listening: The LISTEN instruction allows you to add expectations (with their patterns and actions) on the flux (the receptor's membrane).
Conversations: Some signals are identified as being part of a conversation. Conversations are created with the CONVERSE scoping instruction. You can think of this instruction kind of like the "block" or "scope" construct that exists in most programming languages which creates a lexical scope for variables. CONVERSE creates a scope for sending and listening to signals. CONVERSE instructions can be nested, such that the system recognizes one conversation as a sub-part of another. The system also allows having multiple SAY/REQUEST instructions inside of a conversation to different destinations. This provides underlying support for various multi-party protocols.
Sync/Async: The signaling instructions have waiting and non-waiting versions. This allows you to program in both synchronous and asynchronous style. For example the REQUEST instruction has an optional action parameter. If provided, the REQUEST instruction reduces to the request signal's identifier, and the action process gets executed sometime later when the response arrives. Otherwise the tree with the REQUEST instruction goes into the waiting state and later, when the response message arrives, reduces directly to its body. The action parameter to the LISTEN instruction is similarly optional and indicates whether the tree with the LISTEN should wait or run asynchronously. Similarly for conversations, the CONVERSE instruction can be set to wait or not for the conversation to be completed.
Addressing: Receptor addressing in Ceptr has to take into account the fact that receptors exist inside of other receptors. In other words, the network topology isn't a two dimensional plane. Also, as a programmer, it helps to remember that the "place" where programs execute is in the membrane of the receptor. Thus you can visualize that any signals you generate are either internally bound, i.e. to the inside of the receptor, externally bound to a peer receptor inside the same parent, or bound to some receptor outside the parent. This means that each receptor gets to define its own address space to manage the coherence of messaging inside itself, as well as what messages go in and out. More on addressing in a future post...
Here's a summary of the instructions from the Ceptr instruction set that have to do with signalling:
|SAY||to_address, key_by_aspect, over_carrier, message||Reduces to the signal's identifier|
|REQUEST||Of_address, key_by_aspect, on_carrier, message, expect_response_on, until, *action||If action provided then request reduces to the request's signal identifier. if no callback, then blocks and reduces to the response's message.|
|RESPOND||respond_on_carrier, response_message||Reduces to the response signal's identifier|
|LISTEN||at_aspect, for_carrier, match_pattern, *action, *with_params, *until_end_conditions||If action provided, reduces to REDUCTION_ERROR_SYMBOL(NULL_SYMBOL) and adds an expectation with the pattern to the flux. If no action, then builds a special END_CONDITION of COUNT=1 and blocks processing and then later reduces to the first signal that matches the pattern|
|CONVERSE||scope, *end_conditions, *wait, *topic Defaults: end_conditions:UNLIMITED wait: FALSE |
|Use end_conditions specify when the system will terminate the conversation. Use wait to specify whether the CONVERSE instruction should block waiting for the conversation to complete. If you use asynchronous REQUEST or LISTEN instructions in the scope the conversation and you don't set wait to TRUE you will want to use the THIS_SCOPE instruction to get the conversation identifier so you can call the COMPLETE instruction someplace later or the conversation will never get cleaned up.|
|COMPLETE||with_something, *conversation_ident||Cleans up a conversation and causes the CONVERSE instruction to reduce to value of with_something. If conversation_ident provided, completes that conversation, otherwise assumes the value of THIS_SCOPE. If the specified conversation doesn't exist (i.e. because it was cleaned up by its end conditions) COMPLETE will invoke the error handler. Note that it's possible that the CONVERSE instruction was already reduced, in which case the value of with_someting will get ignored. If the COMPLETE instruction is not inside the CONVERSE scope, it will be reduced to the value of conversation_ident|
|THIS_SCOPE||Reduces to the identifier of the current conversation. Executing THIS_SCOPE not somewhere inside a CONVERSE will invoke the error handler.|
|SELF_ADDR||Reduces to the receptor address of the current receptor.|