Pyli/Methods/Code Flow

From Jonathan Gardner's Tech Wiki
< Pyli‎ | Methods
Jump to: navigation, search

if, cond, when, unless, case

The if, cond, when, and unless functions allow the program to branch on conditions. Conditions are simply expressions that signal whether one branch or another should be followed.

Recall that everything but False is considered True. (See Pyli/Built-in Types#Truth)

if

(if condition when-true {when-false})

The if function is the most basic of the four. It has two or three arguments. The first argument is the condition. If the condition is true, then the second argument is evaluated and returned. If the condition is false, then the third argument is evaluated and returned. If there is no third argument, then False is returned.

Note that all expressions of a function are evaluated, so you will likely want to ampersand-quote the when-true and when-false parameters.

# Print out what sign x is.
(if (plus? x)
    &(print "x is positive")
    &(if (minus? x)
         &(print "x is negative")
         &(print "x is neither positive nor negative")))

Note that the when-true and when-false expressions are single expressions, so you should use progn to bunch multiple statements into a single statement.

cond

cond is especially useful as a replacement for the typical "if/else if/else if/else" structure found in Algol-inspired languages. It takes multiple Vectors. Each vector has two or more items. Each Vector is tested one after the other, until one is found where the first item evaluates to a true result. When this is the case, the rest of the items in the Vector are evaluated and the final one returned. To signal the final "else" clause, which is a catch-all, simply use True for the condition.

(cond &(cond expr ...)
      &(cond expr ...)
      ...)

Please be careful about quoting. Keep in mind that Vectors are passed as parameters to cond. The first item is evaluated, and if true, then all the remaining items are evaluated in turn. Thus, the Vectors should contain items, all of which may be evaluated.

(cond &((plus? x)  (print "x is positive"))
      &((minus? x) (print "x is negative"))
      &(True       (print "x is neither positive nor negative"))

Note that you don't have to use ampersand-quoting. If you are careful, you can use square-vectors.

(cond [&(plus? x)  &(print "x is positive")]
      [&(minus? x) &(print "x is negative")]
      [&True       &(print "x is neither positive nor negative")]

when

when is a shortcut. It is used to test only for true conditions, and execute a series of expressions if it is true. Otherwise, it returns False.

(when condition
      expr ...)

unless

unless is exactly like when, except it tests for the negative condition.

(unless condition
        expr ...)

case

(TODO: case sounds very familiar, almost like a subcase of for. Identify whether case can be handled by a for expression.)

case is used to dispatch based on a value. All the items in a case expression are tested for equality with the first expression. The first one that is equal will have all of the remaining items evaluated, with the final one returned. If nothing matches, False is returned.

(case test-expression
      &(test-value expr ...)
      &(test-value expr ...)
      ...)

If you need more generic testing than case can provide, try cond.

progn, prog1

  • (prog1 ...): Evaluates all the statements, but returns the value of the first one.
  • (progn ...): Evaluate all the statements and return the value of the last one.

If you want something like prog3, do this:

(progn
  (statement 1)
  (statement 2)
  (prog1
    (statement 3)
    (statement 4)
    ...))

Stack Manipulation

NOTE: Maybe not do this...

  • *stack*: The current stack.
  • (set! *stack* foo): Changes the current stack. Execution begins immediately. Be careful what you wish for.
  • (set-stack! var): Sets var to the stack representing the action to follow (set-stack! var).
  • (call-stack! var): Calls the stack, with a copy of var, so as to leave var alone.

TODO: Need a way to change the stack. Note that the only stack capture that are worthwhile are to evaluate the next expression, if any.

EXAMPLE: Writing a simple iterator.

(define foo)
(define bar 0)
((fn () (set-stack! foo) (+! bar 1)))
# > 0
# bar is now 1.
(call-stack! foo)
# > 1
# bar is now 2

Label

This needs to live in the dynamic environment. It makes no sense in the lexical one.

(return) when there is no label can simply put the "done" state on the stack, and thus cause the value to be returned from everything. Another way to do this is put a master label around the entire program.

  • (label name body...): Executes the body with the label of this expression being name. Also sets a local variable *innermost-label*.
  • (restart [label]): Restarts the label represented with the name label, or *innermost-label*.
  • (return [value] [label]): Returns from the label (default *innermost-label*) with value (default False).

TODO: Functions and macros MUST define a label for the function. It is defined as *innermost-function*.

For

For is very powerful.

  • (for ...): For loop. Like loop in Common Lisp, but a bit simpler. See Pyli/Methods/For