Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorials:advanced:task_trees [2015/06/09 13:54] – Added content about de/serialization. mpomarlan | tutorials:advanced:task_trees [2020/09/17 14:09] (current) – [Plan transformations: task trees, code replacement, and serialization] gkazhoya | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Plan transformations: | ====== Plan transformations: | ||
- | **Description: | + | **Disclaimer: |
+ | |||
+ | **Description: | ||
===== CRAM task trees ===== | ===== CRAM task trees ===== | ||
Line 277: | Line 279: | ||
If you inspect the task tree for ptr-fail-handle now, you will see there is no :CALL 2 node. Unlike with-failure-handling, | If you inspect the task tree for ptr-fail-handle now, you will see there is no :CALL 2 node. Unlike with-failure-handling, | ||
+ | |||
+ | ===== Using projection runs to guide plan transformation ===== | ||
+ | |||
+ | Besides reacting to runtime conditions, another possible use of plan transformation is to try out several plan structures "in simulation" | ||
+ | |||
+ | <code lisp> | ||
+ | (swank: | ||
+ | (in-package :cpl-impl) | ||
+ | (cram-projection: | ||
+ | |||
+ | (def-top-level-cram-function tryprj () | ||
+ | (cram-projection: | ||
+ | (progn | ||
+ | (format T "Got result ~A~%Now retrying for real.~%" | ||
+ | (setf *in-projection-environment* nil) | ||
+ | (retry)) | ||
+ | (format T " | ||
+ | ' | ||
+ | nil)) | ||
+ | (format T "Doing stuff.~%" | ||
+ | | ||
+ | (tryprj) | ||
+ | </ | ||
+ | |||
+ | The result from tryprj should look like this: | ||
+ | |||
+ | <code lisp> | ||
+ | Working in environment EXAMPLE-ENV | ||
+ | Doing stuff. | ||
+ | Got result # | ||
+ | :NAME EXAMPLE-ENV | ||
+ | :RESULT (NIL) | ||
+ | : | ||
+ | . #< | ||
+ | Now retrying for real. | ||
+ | Working in environment NIL | ||
+ | Doing stuff. | ||
+ | NIL | ||
+ | </ | ||
+ | |||
+ | We'll now look at what happens and explain the code inside tryprj. | ||
+ | |||
+ | The parameters for cram-projection: | ||
+ | |||
+ | * a PROJECTION-ENVIRONMENT-NAME (same semantics of the parameter as with the with-projection-environment macro: the parameter will be converted to a symbol so you must not write ' | ||
+ | * a TRANSFORMATION-CLAUSE in which the plan should analyze the results from projection, decide whether/how to transform BODY, decide whether to run in projection or not. Some notes on TRANSFORMATION-CLAUSE: | ||
+ | * will typically be a block of code inside a PROGN. | ||
+ | * will typically end in RETRY. Ending in RETURN is also possible. In that case, TRANSFORMATION-CLAUSE will not call BODY again, but instead the macro will return NIL (or whatever value is in the RETURN S-exp) to its caller. | ||
+ | * TRANSFORMATION-CLAUSE has access to the results of projection by (cram-projection: | ||
+ | * the path of BODY inside the task tree is accessible to TRANSFORMATION-CLAUSE via cpl-impl: | ||
+ | * cpl-impl: | ||
+ | * any special variables acting as parameters for BODY can be changed in TRANSFORMATION-CLAUSE. In the example, we use cpl-impl: | ||
+ | * if plan transformations should be enabled for BODY, it must contain at least one CRAM-FUNCTION. | ||
+ | * BODY, a list of S-expressions that should be run. The macro return value is the return value of BODY. | ||
+ | |||
+ | Just like with-transformative-failure-handling, | ||
+ | |||
+ | ===== Changing parameters with plan transformation ===== | ||
+ | |||
+ | Remember one of the previous laws of the task tree: | ||
+ | |||
+ | //When a task tree node is run, the value of PARAMETERS in either CODE or (car CODE-REPLACEMENTS) is set to the value of the parameters at the moment of the function call.// | ||
+ | |||
+ | This means that, typically, you can't change parameters through plan transformation as previously implemented in CRAM. However, it's useful to distinguish two types of parameters for a cram-function: | ||
+ | |||
+ | * " | ||
+ | * " | ||
+ | |||
+ | To support " | ||
+ | |||
+ | <code lisp> | ||
+ | (swank: | ||
+ | |||
+ | (defparameter par-1 1) ;; just a couple variables to give some ' | ||
+ | (defparameter par-2 1) | ||
+ | |||
+ | (cpl-impl: | ||
+ | (format T " | ||
+ | (format T " | ||
+ | (format T "PTR parameter is (~a)~%Will now fail, so as to trigger plan transformation.~%" | ||
+ | (cpl:fail ' | ||
+ | |||
+ | (defun compatible-ptr (P &rest args) | ||
+ | (format T " | ||
+ | (format T " | ||
+ | (format T "PTR parameter is (~a)~%Done!~%" | ||
+ | |||
+ | (cpl-impl: | ||
+ | (cpl-impl: | ||
+ | ((cpl-impl: | ||
+ | (let ((code-path (cpl-impl:: | ||
+ | (format t "Will replace code now (and switch the ptr-parameter) ...~%" | ||
+ | (cpl-impl: | ||
+ | (cpl-impl: | ||
+ | (setf par-1 (+ par-1 par-2)) | ||
+ | (setf par-2 (- par-1 par-2)) | ||
+ | (example-ptr " | ||
+ | </ | ||
+ | |||
+ | Of these, example-ptr is the " | ||
+ | |||
+ | ptr-cram-functions are a bit peculiar. When you call them the first time they behave like regular cram functions, and take their parameters from the ones you supplied. When you call them again (meaning, when there' | ||
+ | |||
+ | To actually change the compile time parameter in a node, notice the extra &key parameter in replace-task-code: | ||
+ | |||
+ | <code lisp> | ||
+ | (cpl-impl: | ||
+ | </ | ||
+ | |||
+ | By default the : | ||
+ | |||
+ | In our example here, the compile-time parameter is a string. It can however be anything, including a struct or a list, for when you need to pass on several objects through plan transformation. | ||
+ | |||
+ | Let's run the top-level function and see what happens (trace below shows a copy-paste from the repl window) ... | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (try-ptr) | ||
+ | Inside example-ptr. | ||
+ | Received arguments (2 1 0) | ||
+ | PTR parameter is (Apples) | ||
+ | Will now fail, so as to trigger plan transformation. | ||
+ | Will replace code now (and switch the ptr-parameter) ... | ||
+ | Inside compatible-ptr. | ||
+ | Received arguments (3 2 0) | ||
+ | PTR parameter is (Oranges) | ||
+ | Done! | ||
+ | NIL | ||
+ | CL-USER> (try-ptr) | ||
+ | Inside compatible-ptr. | ||
+ | Received arguments (5 3 0) | ||
+ | PTR parameter is (Oranges) | ||
+ | Done! | ||
+ | NIL | ||
+ | CL-USER> (try-ptr) | ||
+ | Inside compatible-ptr. | ||
+ | Received arguments (8 5 0) | ||
+ | PTR parameter is (Oranges) | ||
+ | Done! | ||
+ | NIL | ||
+ | </ | ||
+ | |||
+ | Notice how the transformed plan persists between plan runs, including the compile-time parameter, even though the run-time parameters change as expected. | ||
===== Task tree serialization ===== | ===== Task tree serialization ===== | ||
Line 320: | Line 464: | ||
(def-top-level-cram-function ptr-fail-handle () | (def-top-level-cram-function ptr-fail-handle () | ||
- | (with-failure-handling | + | (with-transformative-failure-handling |
((plan-failure (f) | ((plan-failure (f) | ||
(let ((code-path (plan-failure/ | (let ((code-path (plan-failure/ |