at:tutorial:metaprogramming
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| at:tutorial:metaprogramming [2007/04/19 20:57] – stimberm | at:tutorial:metaprogramming [2009/11/21 07:44] (current) – tvcutsem | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | < | ||
| - | |||
| ===== Metaprogramming ===== | ===== Metaprogramming ===== | ||
| - | ==== AT Zero - AmbientTalk without syntactic sugar ==== | + | ==== AmbientTalk without syntactic sugar ==== |
| In AmbientTalk, | In AmbientTalk, | ||
| Line 14: | Line 12: | ||
| >>7 | >>7 | ||
| </ | </ | ||
| - | However, these constructs are all **syntactic sugar**. Behind the scenes, they all perform message sends. The following code shows the equivalents of the previous code, but with the actual message sends: | + | However, these constructs are all **syntactic sugar** |
| < | < | ||
| >[5, 6, 7].at(2) | >[5, 6, 7].at(2) | ||
| Line 23: | Line 21: | ||
| >>7 | >>7 | ||
| </ | </ | ||
| + | |||
| + | In the first example above the message //at// is sent to the table **[5, | ||
| ==== Quasiquoting and splicing ==== | ==== Quasiquoting and splicing ==== | ||
| + | Quasiquoting and splicing are an advanced and powerful metaprogramming techniques that control the evaluation process and allow one to manipulate the evaluation process of the abstract syntax tree. | ||
| - | === Qouting | + | === Quoting |
| Any valid AmbientTalk expression can be quoted. This prevents the expression from being evaluated. Instead, it is returned literally. | Any valid AmbientTalk expression can be quoted. This prevents the expression from being evaluated. Instead, it is returned literally. | ||
| Line 59: | Line 60: | ||
| </ | </ | ||
| - | Note that in '' | + | Note that in '' |
| == Expressions == | == Expressions == | ||
| - | To quote a complete expression, it has to be wrapped in parantheses: | + | To quote a complete expression, it has to be wrapped in parentheses: |
| < | < | ||
| Line 78: | Line 79: | ||
| == Statements == | == Statements == | ||
| - | Statements (definitions, | + | Statements (definitions, |
| < | < | ||
| Line 87: | Line 88: | ||
| </ | </ | ||
| - | If you actually want to quote a literal closure, you should use parantheses, | ||
| < | < | ||
| - | `{ | foo | foo := 4 } gives a literal closure | + | Quoting a literal closure can be done in two ways: as an expression: < |
| + | To quote a literal closure | ||
| </ | </ | ||
| Line 101: | Line 102: | ||
| With this construct, all the elements of the literal table are evaluated. By quoting a literal table, all the elements are quoted instead of evaluated: | With this construct, all the elements of the literal table are evaluated. By quoting a literal table, all the elements are quoted instead of evaluated: | ||
| < | < | ||
| - | > | + | > |
| >> | >> | ||
| </ | </ | ||
| Line 127: | Line 128: | ||
| >[ 7, 8, 9, @upTo(4) ] | >[ 7, 8, 9, @upTo(4) ] | ||
| >>[7, 8, 9, 1, 2, 3, 4] | >>[7, 8, 9, 1, 2, 3, 4] | ||
| + | >[ 7, 8, 9, upTo(4) ] | ||
| + | >>[7, 8, 9, [1, 2, 3, 4]] | ||
| </ | </ | ||
| + | In the example above the elements of the table returned by invoking **upTo(4)** are added in place to the table in which the expression was spliced. | ||
| Splicing can also be used in combination with quoting and unquoting. AmbientTalk provides the // | Splicing can also be used in combination with quoting and unquoting. AmbientTalk provides the // | ||
| Line 136: | Line 140: | ||
| >> | >> | ||
| </ | </ | ||
| - | |||
| ==== First-class abstract grammar ==== | ==== First-class abstract grammar ==== | ||
| + | |||
| + | Quoting an AmbientTalk expression results in an the parse tree of that expression. Like any value in AmbientTalk, | ||
| + | |||
| + | < | ||
| + | >`(f(1, 2, 3)).function | ||
| + | >>f | ||
| + | >`(f(1, 2, 3)).arguments | ||
| + | >>[1, 2, 3] | ||
| + | </ | ||
| + | |||
| + | Any abstract grammar element can serve as a prototype for a new one: | ||
| + | < | ||
| + | >def application := `(f(1, 2, 3)) | ||
| + | >> | ||
| + | > | ||
| + | >> | ||
| + | </ | ||
| + | |||
| + | The following example uses meta-programming and reflection to generate a proxy for an object that provides a given interface. The interface' | ||
| + | |||
| + | < | ||
| + | TODO: Update code | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | def isMethodDefinition: | ||
| + | |||
| + | def policyOf: object with: interface { | ||
| + | |||
| + | def policyDefinition := interface.method.bodyExpression.statements; | ||
| + | |||
| + | policyDefinition.each: | ||
| + | if: (isMethodDefinition: | ||
| + | def methodBody := statement.bodyExpression.statements; | ||
| + | if: ((methodBody.length == 1).and: { methodBody[1] == `nil }) then: { | ||
| + | def bodyExpression := `{ #(object) ^ # | ||
| + | statement.bodyExpression := bodyExpression; | ||
| + | } | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | object: interface; | ||
| + | |||
| + | }; | ||
| + | </ | ||
| + | |||
| + | ==== Read / Eval / Print ==== | ||
| + | |||
| + | AmbientTalk reifies the read, eval and print operations. This means that you can read any string and get the responding syntax tree for it, evaluate any syntax tree and get a value for it, and print any value and get a string representation of the value. | ||
| + | |||
| + | < | ||
| + | >read: " | ||
| + | >> | ||
| + | >def result := eval: `(1+2) in: self | ||
| + | >>3 | ||
| + | >print: result | ||
| + | >>" | ||
| + | </ | ||
| + | |||
| + | Eval is a keyworded message that takes another parameter, namely the object in whose scope the expression must be evaluated. | ||
| + | |||
| + | < | ||
| + | >def o := object: { def x := 4 } | ||
| + | >>< | ||
| + | >eval: `x in: o | ||
| + | >>4 | ||
| + | </ | ||
| + | |||
| + | ==== Multi-stage (Generative) Programming ==== | ||
| + | |||
| + | Here's a small example of " | ||
| + | |||
| + | Below is a regular power function. Given two numbers ' | ||
| + | |||
| + | <code javascript> | ||
| + | def pow(x, n) { | ||
| + | if: (n == 0) then: { | ||
| + | 1 | ||
| + | } else: { | ||
| + | x * pow(x,n-1); | ||
| + | } | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | Let's see whether it works: | ||
| + | <code javascript> | ||
| + | system.println(pow(2, | ||
| + | </ | ||
| + | |||
| + | Now, consider the following ' | ||
| + | |||
| + | <code javascript> | ||
| + | def expandPow(var, | ||
| + | if: (n == 0) then: { | ||
| + | `1 // `exp returns an abstract syntax tree for exp | ||
| + | } else: { | ||
| + | // within a quoted expression, #(exp) evaluates exp, | ||
| + | // expects it to return an AST, and embeds that AST | ||
| + | // in the quoted expression | ||
| + | `(#var * # | ||
| + | } | ||
| + | }; | ||
| + | |||
| + | // this prints the expression 'y * y * y * 1' | ||
| + | system.println(" | ||
| + | </ | ||
| + | |||
| + | To be able to use the expression generated by the above function, let's define a small helper function that will embed this expression in a first-class function: | ||
| + | |||
| + | <code javascript> | ||
| + | def powMaker(n) { | ||
| + | def ast := expandPow(`x, | ||
| + | // the built-in function ' | ||
| + | // evaluates the expression in the scope of the given object | ||
| + | // return a function that has the expanded expression as its body: | ||
| + | eval: `({|x| #ast}) in: self; | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | Now we can generate power functions that are fixed in their second argument, but that are more efficient to execute: | ||
| + | |||
| + | <code javascript> | ||
| + | // pow5 is now bound to a function { |x| x*x*x*x*x*1 } | ||
| + | def pow5 := powMaker(5); | ||
| + | |||
| + | system.println(pow5(2)); | ||
| + | </ | ||
| + | |||
| + | You can measure the performance difference by timing the evaluation of both functions: | ||
| + | |||
| + | <code javascript> | ||
| + | import / | ||
| + | system.println(" | ||
| + | system.println(" | ||
| + | </ | ||
| + | |||
| + | The quoting and unquoting mechanism of AmbientTalk makes it really easy to " | ||
| + | |||
| + | The above example is available in the AmbientTalk library, under '/ | ||
at/tutorial/metaprogramming.1177009046.txt.gz · Last modified: (external edit)
