| | 46 | == Functions |
| | 47 | |
| | 48 | There are two kinds of functions in PIL: |
| | 49 | 1. Imperative functions = "procedures" |
| | 50 | 1. Logic functions |
| | 51 | |
| | 52 | === Procedures |
| | 53 | |
| | 54 | Like in C. These can be nested. Obviously they can have side-effects, be nondeterministic, and the behavior can depend on non-local state. A procedure call `f(x1,...,xn)` is an expression that can be used anywhere an expression with side-effects is allowed. Imperative procedures are not values and cannot be assigned to anything. However, as in C, a pointer to a procedure is a first-class value that can be assigned, used as an argument, returned, etc. There is a procedure type, exactly like C's function type, e.g., |
| | 55 | {{{ |
| | 56 | typedef int(int) i2i; |
| | 57 | }}} |
| | 58 | defines `i2i` to be the type "procedure consuming int and returning int". Most commonly, pointers to procedure types will be used, e.g., |
| | 59 | {{{ |
| | 60 | typedef int(int)* i2ip; |
| | 61 | }}} |
| | 62 | defines the type "pointer to procedure from int to int". As in C, a pointer to a procedure can be used in a procedure call. |
| | 63 | |
| | 64 | A procedure definition can be templated: |
| | 65 | {{{ |
| | 66 | <T1,T2,...> <procedure def> |
| | 67 | }}} |
| | 68 | This defines one procedure for each assignment of types to the Ti. |
| | 69 | |
| | 70 | === Logic functions |
| | 71 | |
| | 72 | Typically specified by a $lambda expression. These are first-class objects in PIL: they can be assigned to variables, passed as an argument in a function call (including a procedure call), and can be returned by a function. Each has a type of the form `$fun<U,T1,...,Tn>` (functions from `T1` x ... x `Tn` to `U`). They are side-effect-free. An application of a logic function `f(x1,...,xn)` is an expression that can be used anywhere an expression is allowed. A logic function is not necessarily pure, i.e., the value of an application may depend on any part of the state, not just the arguments. |
| | 73 | |
| | 74 | === Lambda expressions |
| | 75 | |
| | 76 | There are two kinds of lambda expressions. The first kind defines a procedure, the second a logical function. |
| | 77 | |
| | 78 | Procedural lambda syntax: |
| | 79 | {{{ |
| | 80 | $lambda [U1 v1=init1; ... Um vm=initm;] R (T1 x1, ..., Tn xn) { S1; ... } |
| | 81 | }}} |
| | 82 | where |
| | 83 | * the `Ti` and `Uj` are types |
| | 84 | * the `xi` and `vj` are variables |
| | 85 | * R is a type (the return type), which may be void |
| | 86 | * {S1; ...} is a block (same as in function definition) |
| | 87 | * the only variables that can occur free in the block are the xi and vj |
| | 88 | * if R is not void, the block must return a value of type R |
| | 89 | |
| | 90 | The type of this expression is `R(T1, ..., Tn)`. |
| | 91 | |
| | 92 | Logic function lambda syntax: |
| | 93 | {{{ |
| | 94 | $lambda [U1 v1=init1; ... Um vm=initm;] R (T1 x1, ..., Tn xn) expr |
| | 95 | }}} |
| | 96 | where |
| | 97 | * the `Ti` and `Uj` are types |
| | 98 | * the `xi` and `vj` are variables |
| | 99 | * R is a type (the return type), which cannot be void |
| | 100 | * `expr` is a side-effect-free expression of type R |
| | 101 | * the only variables that can occur free in `expr` are the xi and vj |
| | 102 | |
| | 103 | The type of this expression is `$fun<R, T1, ..., Tn>`. |
| | 104 | |
| | 105 | In both cases, the initializer expressions `initj` are expressions using any variables in scope. |
| | 106 | |
| | 107 | ==== Semantics |
| | 108 | |
| | 109 | FIX THIS: difference between logic and procedural functions... |
| | 110 | |
| | 111 | |
| | 112 | |
| | 113 | The lambda expression is evaluated to yield a *closure*: an ordered pair consisting of a dyscope and an expression. The dyscope has variables `v1`, ..., `vm`, initialized by evaluating `init1`, ..., `initm`. The dyscope has no parent scope, it is associated only with the closure. The expression is `expr`. The dyscope is destroyed whenever it becomes unreachable, i.e., whenever no part of the state is assigned the closure. |
| | 114 | |
| | 115 | A logic function application `f(e1,...,en)` is evaluated by first evaluating the arguments `ei`, then assigning the `ei` to the `xi` and evaluating expr in the closure dyscope. Note that since expr has no side effects, the values of the xi are constant. [Note: a possible change is to allow side-effects in logic functions.] |
| | 116 | |
| 127 | | == Functions |
| 128 | | |
| 129 | | There are two kinds of functions in PIL: |
| 130 | | 1. (Imperative) procedures |
| 131 | | 1. Logic functions |
| 132 | | |
| 133 | | === Procedures |
| 134 | | |
| 135 | | Like in C. These can be nested. Obviously they can have side-effects, be nondeterministic, and the behavior can depend on non-local state. A procedure call `f(x1,...,xn)` is an expression that can be used anywhere an expression with side-effects is allowed. Imperative procedures are not values and cannot be assigned to anything. However, as in C, a pointer to a procedure is a first-class value that can be assigned, used as an argument, returned, etc. There is a procedure type, exactly like C's function type, e.g., |
| 136 | | {{{ |
| 137 | | typedef int(int) i2i; |
| 138 | | }}} |
| 139 | | defines `i2i` to be the type "procedure consuming int and returning int". Most commonly, pointers to procedure types will be used, e.g., |
| 140 | | {{{ |
| 141 | | typedef int(int)* i2ip; |
| 142 | | }}} |
| 143 | | defines the type "pointer to procedure from int to int". As in C, a pointer to a procedure can be used in a procedure call. |
| 144 | | |
| 145 | | A procedure definition can be templated: |
| 146 | | {{{ |
| 147 | | <T1,T2,...> <procedure def> |
| 148 | | }}} |
| 149 | | This defines one procedure for each assignment of types to the Ti. |
| 150 | | |
| 151 | | === Logic functions |
| 152 | | |
| 153 | | Typically specified by a $lambda expression. These are first-class objects in PIL: they can be assigned to variables, passed as an argument in a function call (including a procedure call), and can be returned by a function. Each has a type of the form `$fun<T1,...,Tn;U>` (functions from `T1` x ... x `Tn` to `U`). They are side-effect-free. An application of a logic function `f(x1,...,xn)` is an expression that can be used anywhere an expression is allowed. A logic function is not necessarily pure, i.e., the value of an application may depend on any part of the state, not just the arguments. |
| 154 | | |
| 155 | | === Lambda expressions |
| 156 | | |
| 157 | | There are two kinds of lambda expressions. The first kind defines a procedure, the second a logical function. |
| 158 | | |
| 159 | | Procedural lambda syntax: |
| 160 | | {{{ |
| 161 | | $lambda [U1 v1=init1; ... Um vm=initm;] R (T1 x1, ..., Tn xn) { S1; ... } |
| 162 | | }}} |
| 163 | | where |
| 164 | | * the `Ti` and `Uj` are types |
| 165 | | * the `xi` and `vj` are variables |
| 166 | | * R is a type (the return type), which may be void |
| 167 | | * {S1; ...} is a block (same as in function definition) |
| 168 | | * the only variables that can occur free in the block are the xi and vj |
| 169 | | * if R is not void, the block must return a value of type R |
| 170 | | |
| 171 | | The type of this expression is `R(T1, ..., Tn)`. |
| 172 | | |
| 173 | | Logic function lambda syntax: |
| 174 | | {{{ |
| 175 | | $lambda [U1 v1=init1; ... Um vm=initm;] R (T1 x1, ..., Tn xn) expr |
| 176 | | }}} |
| 177 | | where |
| 178 | | * the `Ti` and `Uj` are types |
| 179 | | * the `xi` and `vj` are variables |
| 180 | | * R is a type (the return type), which cannot be void |
| 181 | | * `expr` is a side-effect-free expression of type R |
| 182 | | * the only variables that can occur free in `expr` are the xi and vj |
| 183 | | |
| 184 | | The type of this expression is `$fun<R, T1, ..., Tn>`. |
| 185 | | |
| 186 | | In both cases, the initializer expressions `initj` are expressions using any variables in scope. |
| 187 | | |
| 188 | | ==== Semantics |
| 189 | | |
| 190 | | FIX THIS: difference between logic and procedural functions... |
| 191 | | |
| 192 | | |
| 193 | | |
| 194 | | The lambda expression is evaluated to yield a *closure*: an ordered pair consisting of a dyscope and an expression. The dyscope has variables `v1`, ..., `vm`, initialized by evaluating `init1`, ..., `initm`. The dyscope has no parent scope, it is associated only with the closure. The expression is `expr`. The dyscope is destroyed whenever it becomes unreachable, i.e., whenever no part of the state is assigned the closure. |
| 195 | | |
| 196 | | A logic function application `f(e1,...,en)` is evaluated by first evaluating the arguments `ei`, then assigning the `ei` to the `xi` and evaluating expr in the closure dyscope. Note that since expr has no side effects, the values of the xi are constant. [Note: a possible change is to allow side-effects in logic functions.] |