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:34] – 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. | ||
- | Quoting | + | Quoting |
* '' | * '' | ||
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: | ||
< | < | ||
- | > | + | > |
>> | >> | ||
+ | </ | ||
+ | |||
+ | === Unquoting === | ||
+ | |||
+ | Inside a quotation, an expression can be unquoted as well. An unquotation escapes from the quotation and causes the unquoted expression to be evaluated. The return value is then used as the quotation of the unquotation. | ||
+ | Unquoting an expression is done with the ''#'' | ||
+ | |||
+ | < | ||
+ | >def msg() { `foo } | ||
+ | >>< | ||
+ | >def arg(n) { n+5 } | ||
+ | >>< | ||
+ | > | ||
+ | >> | ||
</ | </ | ||
=== Splicing === | === Splicing === | ||
+ | Splicing can already be used without quoting: | ||
+ | < | ||
+ | >def upTo(n) { def idx := 0; def tab[n] { idx := idx + 1 } } | ||
+ | >>< | ||
+ | >[ 7, 8, 9, @upTo(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 // | ||
+ | < | ||
+ | > | ||
+ | >> | ||
+ | >`[ @upTo(3), # | ||
+ | >> | ||
+ | </ | ||
==== 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.1177007671.txt.gz · Last modified: 2007/04/19 20:57 (external edit)