www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Dynamic alter-ego of D.

reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
I think adding more dynamic typing to D would be a splendid idea to
further widen the variety of solutions for different problems.
Modular app development is a very good practice and modularity means
dynamicity, which in turn means, that one needs to give up on lots of
sweet stuff like templates, overloading and string mixins.
Variant is the first step towards dynamic alter-ego of D, which is
completely undeveloped currently.
Although my benchmarks show, that variant is quite slow and I'd really
like a version of variant, optimized for ultimate performance.
Also, there are lots of stuff, that i really need at run-time, which i
only have at compile-time:
* Interfaces. dynamic interfaces are very important to highly modular
applications, heavily based on plugins. By dynamic interfaces i mean
the set of attributes and methods of an object, which is only known at
run-time.
* Overloading. A.K.A multimethods. required by dynamic interfaces.
* Dynamic templates. In other words, value-based overloading (since
type is yet another attribute of dynamically typed data).

Dynamic interfaces are also different from static ones because the
interface isn't _implemented_ by a type, it's _matched_ by a type,
which means, that if a type fits into an interface at run-time, i can
safely cast it to that interface type and work with it.

Being able to obtain the dynamic version of a delegate would be great
to pass around generic callbacks.
Oct 25 2011
next sibling parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
Gor Gyolchanyan wrote:
 I think adding more dynamic typing to D would be a splendid idea to
 further widen the variety of solutions for different problems.
 Modular app development is a very good practice and modularity means
 dynamicity, which in turn means, that one needs to give up on lots of
 sweet stuff like templates, overloading and string mixins.
 Variant is the first step towards dynamic alter-ego of D, which is
 completely undeveloped currently.
If Algebraic/Variant could be recognized by the language it would give some interesting possibilities, like Variant array literals: auto array = [10, 2.0, "abc"]; types it as Algebraic!(int, double, string)[]; auto AA = [ "a" : 10, "b" : 2.0, 3 : "abc" ]; types it as Algebraic!(int, double, string)[Algebraic!(string, int)]; for example, that may be used to express JSON structures with literals only: int x, y; auto json = [ "x" : x, "y" : y, "colors" : [ [ r : 255, g : 0, b : 0 ], [ r : 0, g : 0, b : 255 ] ] ]
Oct 25 2011
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 25 Oct 2011 06:31:13 -0400, Piotr Szturmaj <bncrbme jadamspam.pl> wrote:

 Gor Gyolchanyan wrote:
 I think adding more dynamic typing to D would be a splendid idea to
 further widen the variety of solutions for different problems.
 Modular app development is a very good practice and modularity means
 dynamicity, which in turn means, that one needs to give up on lots of
 sweet stuff like templates, overloading and string mixins.
 Variant is the first step towards dynamic alter-ego of D, which is
 completely undeveloped currently.
If Algebraic/Variant could be recognized by the language it would give some interesting possibilities, like Variant array literals: auto array = [10, 2.0, "abc"]; types it as Algebraic!(int, double, string)[]; auto AA = [ "a" : 10, "b" : 2.0, 3 : "abc" ]; types it as Algebraic!(int, double, string)[Algebraic!(string, int)]; for example, that may be used to express JSON structures with literals only: int x, y; auto json = [ "x" : x, "y" : y, "colors" : [ [ r : 255, g : 0, b : 0 ], [ r : 0, g : 0, b : 255 ] ] ]
Why do we need language support? This works in my variant proposal: auto json = Variant( "x",x, "y",y, "colors", [[ "r":255, "g":0, "b":0],[ "r":0, "g":0, "b":255] ] ); Granted if ["r":255,"g":0,"b":0] weren't all int, it would have to be declared Variant("r",255,"g",0,"b",0), but we could always shorten Variant to var or something.
Oct 25 2011
next sibling parent Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
Variant is heavy. It contains and does lots of things, that are not
always required.
a tiny type-less variable is versatile and can be used for any
purposes. It's even type-safe in debug mode.
Also, Variant needs explicit construction, whereas the tiny typeless
variable automatically accepts any value.
Also, Variant does not grant access to underlying raw data, but tiny
typeless value can be taken address of (void*).

On Tue, Oct 25, 2011 at 5:11 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Tue, 25 Oct 2011 06:31:13 -0400, Piotr Szturmaj <bncrbme jadamspam.pl>
 wrote:

 Gor Gyolchanyan wrote:
 I think adding more dynamic typing to D would be a splendid idea to
 further widen the variety of solutions for different problems.
 Modular app development is a very good practice and modularity means
 dynamicity, which in turn means, that one needs to give up on lots of
 sweet stuff like templates, overloading and string mixins.
 Variant is the first step towards dynamic alter-ego of D, which is
 completely undeveloped currently.
If Algebraic/Variant could be recognized by the language it would give some interesting possibilities, like Variant array literals: auto array =3D [10, 2.0, "abc"]; types it as Algebraic!(int, double, string)[]; auto AA =3D [ "a" : 10, "b" : 2.0, 3 : "abc" ]; types it as Algebraic!(int, double, string)[Algebraic!(string, int)]; for example, that may be used to express JSON structures with literals only: int x, y; auto json =3D [ =A0 =A0 "x" : x, =A0 =A0 "y" : y, =A0 =A0 "colors" : [ =A0 =A0 =A0 =A0 [ =A0 =A0 =A0 =A0 =A0 =A0 r : 255, =A0 =A0 =A0 =A0 =A0 =A0 g : 0, =A0 =A0 =A0 =A0 =A0 =A0 b : 0 =A0 =A0 =A0 =A0 ], =A0 =A0 =A0 =A0 [ =A0 =A0 =A0 =A0 =A0 =A0 r : 0, =A0 =A0 =A0 =A0 =A0 =A0 g : 0, =A0 =A0 =A0 =A0 =A0 =A0 b : 255 =A0 =A0 =A0 =A0 ] =A0 =A0 ] ]
Why do we need language support? This works in my variant proposal: auto json =3D Variant( =A0 =A0"x",x, =A0 =A0"y",y, =A0 =A0"colors", [[ =A0 =A0 =A0 =A0"r":255, =A0 =A0 =A0 =A0"g":0, =A0 =A0 =A0 =A0"b":0],[ =A0 =A0 =A0 =A0"r":0, =A0 =A0 =A0 =A0"g":0, =A0 =A0 =A0 =A0"b":255] =A0 =A0] ); Granted if ["r":255,"g":0,"b":0] weren't all int, it would have to be declared Variant("r",255,"g",0,"b",0), but we could always shorten Varian=
t
 to var or something.
Oct 25 2011
prev sibling parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
I need an extremely fast and small typeless container for a single
object, which i can use to implement a fast and efficient dynamic
callback mechanism, where the exact number and types of parameters are
only known to the callback and to the source of the parameter values
and all intermediate manager classes, who hold and return the callback
do not know it.

On Tue, Oct 25, 2011 at 5:20 PM, Gor Gyolchanyan
<gor.f.gyolchanyan gmail.com> wrote:
 Variant is heavy. It contains and does lots of things, that are not
 always required.
 a tiny type-less variable is versatile and can be used for any
 purposes. It's even type-safe in debug mode.
 Also, Variant needs explicit construction, whereas the tiny typeless
 variable automatically accepts any value.
 Also, Variant does not grant access to underlying raw data, but tiny
 typeless value can be taken address of (void*).

 On Tue, Oct 25, 2011 at 5:11 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Tue, 25 Oct 2011 06:31:13 -0400, Piotr Szturmaj <bncrbme jadamspam.pl=
 wrote:

 Gor Gyolchanyan wrote:
 I think adding more dynamic typing to D would be a splendid idea to
 further widen the variety of solutions for different problems.
 Modular app development is a very good practice and modularity means
 dynamicity, which in turn means, that one needs to give up on lots of
 sweet stuff like templates, overloading and string mixins.
 Variant is the first step towards dynamic alter-ego of D, which is
 completely undeveloped currently.
If Algebraic/Variant could be recognized by the language it would give some interesting possibilities, like Variant array literals: auto array =3D [10, 2.0, "abc"]; types it as Algebraic!(int, double, string)[]; auto AA =3D [ "a" : 10, "b" : 2.0, 3 : "abc" ]; types it as Algebraic!(int, double, string)[Algebraic!(string, int)]; for example, that may be used to express JSON structures with literals only: int x, y; auto json =3D [ =A0 =A0 "x" : x, =A0 =A0 "y" : y, =A0 =A0 "colors" : [ =A0 =A0 =A0 =A0 [ =A0 =A0 =A0 =A0 =A0 =A0 r : 255, =A0 =A0 =A0 =A0 =A0 =A0 g : 0, =A0 =A0 =A0 =A0 =A0 =A0 b : 0 =A0 =A0 =A0 =A0 ], =A0 =A0 =A0 =A0 [ =A0 =A0 =A0 =A0 =A0 =A0 r : 0, =A0 =A0 =A0 =A0 =A0 =A0 g : 0, =A0 =A0 =A0 =A0 =A0 =A0 b : 255 =A0 =A0 =A0 =A0 ] =A0 =A0 ] ]
Why do we need language support? This works in my variant proposal: auto json =3D Variant( =A0 =A0"x",x, =A0 =A0"y",y, =A0 =A0"colors", [[ =A0 =A0 =A0 =A0"r":255, =A0 =A0 =A0 =A0"g":0, =A0 =A0 =A0 =A0"b":0],[ =A0 =A0 =A0 =A0"r":0, =A0 =A0 =A0 =A0"g":0, =A0 =A0 =A0 =A0"b":255] =A0 =A0] ); Granted if ["r":255,"g":0,"b":0] weren't all int, it would have to be declared Variant("r",255,"g",0,"b",0), but we could always shorten Varia=
nt
 to var or something.
Oct 25 2011
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 25 Oct 2011 09:23:10 -0400, Gor Gyolchanyan
<gor.f.gyolchanyan gmail.com> wrote:
 I need an extremely fast and small typeless container for a single
 object, which i can use to implement a fast and efficient dynamic
 callback mechanism, where the exact number and types of parameters are
 only known to the callback and to the source of the parameter values
 and all intermediate manager classes, who hold and return the callback
 do not know it.

 On Tue, Oct 25, 2011 at 5:20 PM, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:
 Variant is heavy. It contains and does lots of things, that are not
 always required.
 a tiny type-less variable is versatile and can be used for any
 purposes. It's even type-safe in debug mode.
 Also, Variant needs explicit construction, whereas the tiny typeless
 variable automatically accepts any value.
 Also, Variant does not grant access to underlying raw data, but tiny
 typeless value can be taken address of (void*).
Just because something is feature-full, doesn't make it heavy weight. But I guess over a raw tagged-union with a limited interface, it would be considered heavy. But, I don't understand you concept of a type-less variable. Truly type-less variables don't exist in any language; you always have a pluripotent dynamic type like variant under the hood.
Oct 25 2011
next sibling parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
That's the point. You don't always need to carry around your type.
Also because you might do it in a specific and very efficient way.
Imposing a single way of storing the type is a very inflexible decision.
The type check may also be done at different points, after which it's
not necessary to carry around.
Also, as i said before, type checking WILL be done in debug mode and
dropped in release mode.
Variant could be built on that typeless value concept to ensure type
safety at all times.

On Tue, Oct 25, 2011 at 7:33 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Tue, 25 Oct 2011 09:23:10 -0400, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:
 I need an extremely fast and small typeless container for a single
 object, which i can use to implement a fast and efficient dynamic
 callback mechanism, where the exact number and types of parameters are
 only known to the callback and to the source of the parameter values
 and all intermediate manager classes, who hold and return the callback
 do not know it.

 On Tue, Oct 25, 2011 at 5:20 PM, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:
 Variant is heavy. It contains and does lots of things, that are not
 always required.
 a tiny type-less variable is versatile and can be used for any
 purposes. It's even type-safe in debug mode.
 Also, Variant needs explicit construction, whereas the tiny typeless
 variable automatically accepts any value.
 Also, Variant does not grant access to underlying raw data, but tiny
 typeless value can be taken address of (void*).
Just because something is feature-full, doesn't make it heavy weight. But I guess over a raw tagged-union with a limited interface, it would be considered heavy. But, I don't understand you concept of a type-less variable. Truly type-less variables don't exist in any language; you always have a pluripotent dynamic type like variant under the hood.
Oct 25 2011
parent deadalnix <deadalnix gmail.com> writes:
Le 25/10/2011 17:39, Gor Gyolchanyan a écrit :
 That's the point. You don't always need to carry around your type.
 Also because you might do it in a specific and very efficient way.
 Imposing a single way of storing the type is a very inflexible decision.
 The type check may also be done at different points, after which it's
 not necessary to carry around.
 Also, as i said before, type checking WILL be done in debug mode and
 dropped in release mode.
 Variant could be built on that typeless value concept to ensure type
 safety at all times.
Well I don't kbnow how you can handle type safety on a non typed variable. That doesn't make sense to me. What you want to to will end-up to be more complicated than Variant, but without the garanty that strong typing gives you. You'll losse on every facelets of the problem.
Oct 26 2011
prev sibling parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
another example is dynamic callbacks.
You may carry around typeid of the function signature and a struct,
containing parameter values for it, which also contains the typeid of
it's target function's signature.
If you don't impose type information on the typeless values, i will be
able to check the types of the signatures (possibly by a hash value
for efficiency) and i won't need to check the types of each parameter
(since i'll be passing around array of typeless objects).


On Tue, Oct 25, 2011 at 7:33 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Tue, 25 Oct 2011 09:23:10 -0400, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:
 I need an extremely fast and small typeless container for a single
 object, which i can use to implement a fast and efficient dynamic
 callback mechanism, where the exact number and types of parameters are
 only known to the callback and to the source of the parameter values
 and all intermediate manager classes, who hold and return the callback
 do not know it.

 On Tue, Oct 25, 2011 at 5:20 PM, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:
 Variant is heavy. It contains and does lots of things, that are not
 always required.
 a tiny type-less variable is versatile and can be used for any
 purposes. It's even type-safe in debug mode.
 Also, Variant needs explicit construction, whereas the tiny typeless
 variable automatically accepts any value.
 Also, Variant does not grant access to underlying raw data, but tiny
 typeless value can be taken address of (void*).
Just because something is feature-full, doesn't make it heavy weight. But I guess over a raw tagged-union with a limited interface, it would be considered heavy. But, I don't understand you concept of a type-less variable. Truly type-less variables don't exist in any language; you always have a pluripotent dynamic type like variant under the hood.
Oct 25 2011
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 25 Oct 2011 11:45:48 -0400, Gor Gyolchanyan
<gor.f.gyolchanyan gmail.com> wrote:
 another example is dynamic callbacks.
 You may carry around typeid of the function signature and a struct,
 containing parameter values for it, which also contains the typeid of
 it's target function's signature.
 If you don't impose type information on the typeless values, i will be
 able to check the types of the signatures (possibly by a hash value
 for efficiency) and i won't need to check the types of each parameter
 (since i'll be passing around array of typeless objects).
I'm confused. So delegate don't work because? What about unions? Or casting a ptr into an array of raw bytes? Do you call the callback like 'dg();' or 'dg(x,y,z);'? If the latter, what about implicit variable conversions? At some level, either you hold the type in the type system, or as a tag somewhere.
Oct 25 2011
parent Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
 So delegate don't work because?
Delegates won't work this way because the parameters aren't bound and are of unknown quantity and types.
 What about unions?
Unions allow only a limited number of types.
 Or casting a ptr into an array of raw bytes?
This won't always work, because static arrays must be converted to dynamic arrays and structs of size, greater, then (2*size_t.sizeof) must be passed by pointer. Otherwise it'll be the exact same thing as i'm trying to say.
 Do you call the callback like 'dg();' or 'dg(x,y,z);'?
The second one. The callback takes arguments, only known to the one calling it, not the one storing and returning it.
 At some level, either you hold the type in the type system, or as a tag
somewhere.
Right, and all i'm trying to say is, that "somewhere" must be customizable. Moreover, that "somewhere" may not exist along the object's entire lifetime. The example with the dynamic callback showed when the types are not stored. On Tue, Oct 25, 2011 at 8:20 PM, Robert Jacques <sandford jhu.edu> wrote:
 On Tue, 25 Oct 2011 11:45:48 -0400, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:
 another example is dynamic callbacks.
 You may carry around typeid of the function signature and a struct,
 containing parameter values for it, which also contains the typeid of
 it's target function's signature.
 If you don't impose type information on the typeless values, i will be
 able to check the types of the signatures (possibly by a hash value
 for efficiency) and i won't need to check the types of each parameter
 (since i'll be passing around array of typeless objects).
I'm confused. So delegate don't work because? What about unions? Or casting a ptr into an array of raw bytes? Do you call the callback like 'dg();' or 'dg(x,y,z);'? If the latter, what about implicit variable conversions? At some level, either you hold the type in the type system, or as a tag somewhere.
Oct 25 2011
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 25 Oct 2011 06:00:28 -0400, Gor Gyolchanyan  
<gor.f.gyolchanyan gmail.com> wrote:

 I think adding more dynamic typing to D would be a splendid idea to
 further widen the variety of solutions for different problems.
 Modular app development is a very good practice and modularity means
 dynamicity, which in turn means, that one needs to give up on lots of
 sweet stuff like templates, overloading and string mixins.
 Variant is the first step towards dynamic alter-ego of D, which is
 completely undeveloped currently.
 Although my benchmarks show, that variant is quite slow and I'd really
 like a version of variant, optimized for ultimate performance.
 Also, there are lots of stuff, that i really need at run-time, which i
 only have at compile-time:
 * Interfaces. dynamic interfaces are very important to highly modular
 applications, heavily based on plugins. By dynamic interfaces i mean
 the set of attributes and methods of an object, which is only known at
 run-time.
 * Overloading. A.K.A multimethods. required by dynamic interfaces.
 * Dynamic templates. In other words, value-based overloading (since
 type is yet another attribute of dynamically typed data).

 Dynamic interfaces are also different from static ones because the
 interface isn't _implemented_ by a type, it's _matched_ by a type,
 which means, that if a type fits into an interface at run-time, i can
 safely cast it to that interface type and work with it.

 Being able to obtain the dynamic version of a delegate would be great
 to pass around generic callbacks.
Have you looked into opDispatch? I think with opDispatch and some Variant[string] member, you should be able to implement fully dynamic types. I think even some have done this in the past (even if only for proof of concept). -Steve
Oct 26 2011
parent reply Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
1. opDispatch is no good for overloading when the set of methods are
defined at run-time.
2. opDIspatch doesn't cover operators (why?).

On Wed, Oct 26, 2011 at 6:09 PM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 On Tue, 25 Oct 2011 06:00:28 -0400, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:

 I think adding more dynamic typing to D would be a splendid idea to
 further widen the variety of solutions for different problems.
 Modular app development is a very good practice and modularity means
 dynamicity, which in turn means, that one needs to give up on lots of
 sweet stuff like templates, overloading and string mixins.
 Variant is the first step towards dynamic alter-ego of D, which is
 completely undeveloped currently.
 Although my benchmarks show, that variant is quite slow and I'd really
 like a version of variant, optimized for ultimate performance.
 Also, there are lots of stuff, that i really need at run-time, which i
 only have at compile-time:
 * Interfaces. dynamic interfaces are very important to highly modular
 applications, heavily based on plugins. By dynamic interfaces i mean
 the set of attributes and methods of an object, which is only known at
 run-time.
 * Overloading. A.K.A multimethods. required by dynamic interfaces.
 * Dynamic templates. In other words, value-based overloading (since
 type is yet another attribute of dynamically typed data).

 Dynamic interfaces are also different from static ones because the
 interface isn't _implemented_ by a type, it's _matched_ by a type,
 which means, that if a type fits into an interface at run-time, i can
 safely cast it to that interface type and work with it.

 Being able to obtain the dynamic version of a delegate would be great
 to pass around generic callbacks.
Have you looked into opDispatch? I think with opDispatch and some Variant[string] member, you should be ab=
le
 to implement fully dynamic types. =A0I think even some have done this in =
the
 past (even if only for proof of concept).

 -Steve
Oct 26 2011
next sibling parent Adam Ruppe <destructionator gmail.com> writes:
You can use opDispatch to make runtime methods and properties
by having it forward to a function to do the lookup.

Something alone these lines:

DynamicObject delegate(DynamicObject[] args) dynamicFunctions;

DynamicObject opDispatch(string name, T...)(T t) {
      if(name !in dynamicFunctions) throw new MethodNotFoundException(name);
      DynamicObject[] args;
      foreach(arg; t)
           args ~= new DynamicObject(arg);

      return dynamicFunctions[name](args);
}


I've done a more complete implementation before, but don't have
it on hand right now.
Oct 26 2011
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 26 Oct 2011 10:15:33 -0400, Gor Gyolchanyan  
<gor.f.gyolchanyan gmail.com> wrote:

 1. opDispatch is no good for overloading when the set of methods are
 defined at run-time.
For those cases, you must use the runtime interface that opDispatch will use directly. D does not have a way to call a function by string at runtime. call("functionName", arg1, arg2, arg3) opDispatch simply provides a way to write cleaner code when you know the name of the function. Even in dynamic languages, it's uncommon to call functions based on runtime strings.
 2. opDIspatch doesn't cover operators (why?).
operators are not function symbols, and are already covered by templates. It should be as simple as: opBinary(string s, T...)(T args) if(isValidOperator!s) { call(s, args); } -Steve
Oct 26 2011
parent Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
I know how opDispatch works. opDispatch is purely a syntax sugar (a
really neat one, IMO).
What I'm talking about is a way to choose between a set of functions,
based on the parameters.
It's basically dynamic overloading, but with additional ability to
overload, based on values (much like template constraints).
opDispatch, then could be a good way of hiding ugly code like
call("methodName", ...);

I never said, that i want this stuff to be built-in. THis is one of
those cool things, that many people could use as library solutions.
Such dynamic functions with powerful dynamic overloading and the help
of opDispatch would be an unprecedented awesome solution to event
handlers and callbacks.

On Wed, Oct 26, 2011 at 6:26 PM, Steven Schveighoffer
<schveiguy yahoo.com> wrote:
 On Wed, 26 Oct 2011 10:15:33 -0400, Gor Gyolchanyan
 <gor.f.gyolchanyan gmail.com> wrote:

 1. opDispatch is no good for overloading when the set of methods are
 defined at run-time.
For those cases, you must use the runtime interface that opDispatch will =
use
 directly. =A0D does not have a way to call a function by string at runtim=
e.
 call("functionName", arg1, arg2, arg3)

 opDispatch simply provides a way to write cleaner code when you know the
 name of the function.

 Even in dynamic languages, it's uncommon to call functions based on runti=
me
 strings.

 2. opDIspatch doesn't cover operators (why?).
operators are not function symbols, and are already covered by templates. =A0It should be as simple as: opBinary(string s, T...)(T args) if(isValidOperator!s) { =A0 call(s, args); } -Steve
Oct 26 2011