-
Notifications
You must be signed in to change notification settings - Fork 89
Late Binding Macro
Late Binding Macro changes nemerle syntax to allow use of so called duck typing.
Implementation initially available from Snaury's page is now commited and maintained in Nemerle's standard distribution. The next release will allow you to use it without any additional code.
After adding using Nemerle.Late to your code you will get two statements which you can use in your code: late and nolate.
Using macros is as simple as:
late(''late-expr'') late late-expr nolate(''expr'') nolate expr
Where late-expr and expr are any valid Nemerle expressions, with the exception that late-expr is processed in a special way.
def create_some_application() {
System.Activator.CreateInstance(System.Type.GetTypeFromProgID("Some.Application"))
}
def app = create_some_application();
def file = late app.OpenFile("somefile.txt", openmode = "rw");
late {
def updated = file.Record[0].Update() :> bool;
file.Record[1].MarkedForDeletion = updated;
}
late macro scans deeply inside late-expr and in most cases treats late-expr as a normal nemerle expression. However, in some special cases it does late binding. Here are these cases:
ref_or_out = 'ref' | 'out'
parameter = [ IDENTIFIER '=' ] [ ref_or_out ] late-expr
parameters = [ parameter [ { ',' parameter } ] ]
set_property = late-bound-expr '.' IDENTIFIER '=' late-expr
set_named_indexer = late-bound-expr '.' IDENTIFIER '[' parameters ']' '=' late-expr
set_default_indexer = late-bound-expr [ '.' ] '[' parameters ']' '=' late-expr
get_property = late-bound-expr '.' IDENTIFIER
get_named_property = late-bound-expr '.' IDENTIFIER '[' parameters ']'
get_default_property = late-bound-expr [ '.' ] '[' parameters ']' '=' late-expr
invoke_method = late-bound-expr '.' IDENTIFIER '(' parameters ')'
do_not_process = 'late' '(' late-expr ')' | 'late' late-expr | 'nolate' '(' expr ')' | 'nolate' expr
Where late-bound-expr is any expression except this, base or existing class name.
Note: Optional dot for default_indexer is a feature to make constructs like this possible:
def o = System.Windows.Forms.Form();
def r = late o.Name.[0]; // obtains first letter of property Name
Because if you write late o.Name[0] it means you want to access indexer Name on Form (which does not exist), not default indexer on property Name on Form.
Note: When late macro sees a do_not_process expression it does not look inside of it and uses it as is. This allows one to write embedded late expressions, as well as temporarily turning off late-binding:
late {
// here the first Count is retrieved using late binding, second Count retrieved statically
printf("%d - %d\n", this.Controls.Count, nolate this.Controls.Count);
}
Named parameters are always substituted for parameters of target call. However one must note, that because of this, unnamed parameters do not have a specific place bound to them, i.e.:
public class SomeClass {
public SomeMethod(a : int, b : int, c : int, d : int) : void { ... }
}
def o = SomeClass()
_ = late(o).SomeMethod(1, a = 2, 3, c = 4); // note, it called: SomeMethod(2, 1, 4, 3)
Warning: There is a bug in DefaultBinder on MS.NET which sometimes binds named parameters in wrong places. The advise here will be to avoid using named parameters if you are specifying more than two named parameters and when you are doing late-binding on CLI classes (i.e. this bug does not happen on MarshalByRef and ComObject targets). An example when you can encounter this bug:
#pragma indent
using Nemerle.IO
using Nemerle.Late
public class TestClass
public Method(a : int, b : int, c : int) : int
printf("TestClass.TestMethod(%d,%d,%d)\n", a, b, c)
a + b + c
def o = TestClass() : object
def n = late o.Method(c = 1, a = 2, b = 3)
In the above example TestClass.Method(3,1,2) was called on MS.NET.
- Operators are not made dynamic, so using them on late bound methods will probably yield error from compiler about using operator on object types. We are still considering feasibility of searching the proper op_Addition - like methods in given types for execution.