digitalmars.D - Idea : Expression Type
- Xinok (24/24) Jan 31 2007 I've held off this idea for a little while now, but I think it could rea...
- Andrei Alexandrescu (See Website for Email) (26/46) Jan 31 2007 Walter had the idea of allowing an expression to bind to an alias, and
- Kyle Furlong (4/59) Feb 01 2007 This seems to me to be an obvious good extension to the template system....
- Don Clugston (8/68) Feb 01 2007 The name mangling looks tricky. I think an expression alias would have
-
Kyle Furlong
(2/71)
Feb 01 2007
I suppose I meant easy use, not easy implementation.
- Johan Granberg (20/64) Jan 31 2007 votes++
- Xinok (16/58) Feb 01 2007 I think loops would be best left to functions. Just my opinion anyways, ...
- Andrei Alexandrescu (See Website For Email) (11/41) Feb 01 2007 Nonono. As I said (in the paragraph you quoted above), expressions could...
- Vladimir Panteleev (42/49) Feb 02 2007 liases though, an expression can accept non-const arguments.
- Don Clugston (6/24) Feb 02 2007 ???
- Vladimir Panteleev (6/11) Feb 02 2007 AFAIK, even with -inline, the compiler doesn't automatically inline func...
- Andrei Alexandrescu (See Website For Email) (4/19) Feb 02 2007 They are different beasts. Expression transformations specify syntactic
- Xinok (4/4) Feb 02 2007 Functions aren't powerful enough to be used in place of expressions.
- Andrei Alexandrescu (See Website For Email) (10/15) Feb 02 2007 Conditionals always have one return type that can be detected as
- Don Clugston (7/32) Feb 02 2007 That's already possible, albeit clumsy. You can duplicate the expression...
- Andrei Alexandrescu (See Website For Email) (7/33) Feb 02 2007 Oh, yes, it's definitely possible. "Worrying about" does not mean "can't...
- Don Clugston (3/42) Feb 02 2007 Agreed. I think mixins were supposed to be a replacement for macros, but...
I've held off this idea for a little while now, but I think it could really be a nice addition to D. Expressions are basically aliases which can accept arguments. Unlike aliases though, an expression can accept non-const arguments. expression mul(a, b) = a * b; // Syntax is different from aliases, for good reason... int main(){ int a = 35, b = 62; a = mul(a, b); } 'mul' isn't a constant expression. The expression is inlined into the code, it doesn't call a function. So basically, expressions are inline functions. BUT, because D lacks references (which I'll never be able to understand), functions can't return l-values. Expressions can be treated as so though. One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value. Expressions can also be used for simpler operations, such as dereferencing an array or pointer, to make code easier to read and write: int* ptr = new int; expression exp = *ptr; exp = 62; int[] arr = [15, 30, 45, 60]; expression val = arr[2]; val = 30; Expressions can also handle constants, so it could be used as an alias which can take arguments. (Maybe require const property?) const expression ptr(T) = T*; -- Expressions are NOT mixins! You can't use undeclared symbols in an expression. -- Expressions are NOT functions! The expression is inlined into the code.
Jan 31 2007
Xinok wrote:I've held off this idea for a little while now, but I think it could really be a nice addition to D. Expressions are basically aliases which can accept arguments. Unlike aliases though, an expression can accept non-const arguments. expression mul(a, b) = a * b; // Syntax is different from aliases, for good reason... int main(){ int a = 35, b = 62; a = mul(a, b); } 'mul' isn't a constant expression. The expression is inlined into the code, it doesn't call a function. So basically, expressions are inline functions. BUT, because D lacks references (which I'll never be able to understand), functions can't return l-values. Expressions can be treated as so though.Walter had the idea of allowing an expression to bind to an alias, and to allow the following: template mul(alias a, alias b) { alias (a * b) mul; } with the effect that you want. (The parens are there to keep alias happy, not to prevent breakage of order of operations.)One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value.Nah, that won't work. It will doubly evaluate one of its arguments. But the feature will allow very simple implementation of my varargs_reduce and varargs_reduce_parallel primitives, e.g.: template varargs_reduce(alias fun) { template impl(alias args...) { static if (args.length == 2) alias fun(args) impl; else alias impl!(fun(args[0], args[1]), args[2..$]) impl; } } This has a similar structure to the varargs_reduce that I posted a while ago, except that it doesn't need to do any type inference - it just replaces the expression and lets the compiler take care of the rest. Andrei
Jan 31 2007
Andrei Alexandrescu (See Website for Email) wrote:Xinok wrote:This seems to me to be an obvious good extension to the template system. With the ability to pass aliases into the template, you gain arbitrary and easy expression composition.I've held off this idea for a little while now, but I think it could really be a nice addition to D. Expressions are basically aliases which can accept arguments. Unlike aliases though, an expression can accept non-const arguments. expression mul(a, b) = a * b; // Syntax is different from aliases, for good reason... int main(){ int a = 35, b = 62; a = mul(a, b); } 'mul' isn't a constant expression. The expression is inlined into the code, it doesn't call a function. So basically, expressions are inline functions. BUT, because D lacks references (which I'll never be able to understand), functions can't return l-values. Expressions can be treated as so though.Walter had the idea of allowing an expression to bind to an alias, and to allow the following: template mul(alias a, alias b) { alias (a * b) mul; } with the effect that you want. (The parens are there to keep alias happy, not to prevent breakage of order of operations.)One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value.Nah, that won't work. It will doubly evaluate one of its arguments. But the feature will allow very simple implementation of my varargs_reduce and varargs_reduce_parallel primitives, e.g.: template varargs_reduce(alias fun) { template impl(alias args...) { static if (args.length == 2) alias fun(args) impl; else alias impl!(fun(args[0], args[1]), args[2..$]) impl; } } This has a similar structure to the varargs_reduce that I posted a while ago, except that it doesn't need to do any type inference - it just replaces the expression and lets the compiler take care of the rest. Andrei
Feb 01 2007
Kyle Furlong wrote:Andrei Alexandrescu (See Website for Email) wrote:The name mangling looks tricky. I think an expression alias would have to behave like a typedef: given alias (a*b) X; alias (a*b) Y; X and Y would not be the same. A name mangling scheme for an arbitrary expression would be a bit of a nightmare.Xinok wrote:This seems to me to be an obvious good extension to the template system. With the ability to pass aliases into the template, you gain arbitrary and easy expression composition.I've held off this idea for a little while now, but I think it could really be a nice addition to D. Expressions are basically aliases which can accept arguments. Unlike aliases though, an expression can accept non-const arguments. expression mul(a, b) = a * b; // Syntax is different from aliases, for good reason... int main(){ int a = 35, b = 62; a = mul(a, b); } 'mul' isn't a constant expression. The expression is inlined into the code, it doesn't call a function. So basically, expressions are inline functions. BUT, because D lacks references (which I'll never be able to understand), functions can't return l-values. Expressions can be treated as so though.Walter had the idea of allowing an expression to bind to an alias, and to allow the following: template mul(alias a, alias b) { alias (a * b) mul; } with the effect that you want. (The parens are there to keep alias happy, not to prevent breakage of order of operations.)One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value.Nah, that won't work. It will doubly evaluate one of its arguments. But the feature will allow very simple implementation of my varargs_reduce and varargs_reduce_parallel primitives, e.g.: template varargs_reduce(alias fun) { template impl(alias args...) { static if (args.length == 2) alias fun(args) impl; else alias impl!(fun(args[0], args[1]), args[2..$]) impl; } } This has a similar structure to the varargs_reduce that I posted a while ago, except that it doesn't need to do any type inference - it just replaces the expression and lets the compiler take care of the rest. Andrei
Feb 01 2007
Don Clugston wrote:Kyle Furlong wrote:I suppose I meant easy use, not easy implementation. <g>Andrei Alexandrescu (See Website for Email) wrote:The name mangling looks tricky. I think an expression alias would have to behave like a typedef: given alias (a*b) X; alias (a*b) Y; X and Y would not be the same. A name mangling scheme for an arbitrary expression would be a bit of a nightmare.Xinok wrote:This seems to me to be an obvious good extension to the template system. With the ability to pass aliases into the template, you gain arbitrary and easy expression composition.I've held off this idea for a little while now, but I think it could really be a nice addition to D. Expressions are basically aliases which can accept arguments. Unlike aliases though, an expression can accept non-const arguments. expression mul(a, b) = a * b; // Syntax is different from aliases, for good reason... int main(){ int a = 35, b = 62; a = mul(a, b); } 'mul' isn't a constant expression. The expression is inlined into the code, it doesn't call a function. So basically, expressions are inline functions. BUT, because D lacks references (which I'll never be able to understand), functions can't return l-values. Expressions can be treated as so though.Walter had the idea of allowing an expression to bind to an alias, and to allow the following: template mul(alias a, alias b) { alias (a * b) mul; } with the effect that you want. (The parens are there to keep alias happy, not to prevent breakage of order of operations.)One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value.Nah, that won't work. It will doubly evaluate one of its arguments. But the feature will allow very simple implementation of my varargs_reduce and varargs_reduce_parallel primitives, e.g.: template varargs_reduce(alias fun) { template impl(alias args...) { static if (args.length == 2) alias fun(args) impl; else alias impl!(fun(args[0], args[1]), args[2..$]) impl; } } This has a similar structure to the varargs_reduce that I posted a while ago, except that it doesn't need to do any type inference - it just replaces the expression and lets the compiler take care of the rest. Andrei
Feb 01 2007
Xinok wrote:I've held off this idea for a little while now, but I think it could really be a nice addition to D. Expressions are basically aliases which can accept arguments. Unlike aliases though, an expression can accept non-const arguments. expression mul(a, b) = a * b; // Syntax is different from aliases, for good reason... int main(){ int a = 35, b = 62; a = mul(a, b); } 'mul' isn't a constant expression. The expression is inlined into the code, it doesn't call a function. So basically, expressions are inline functions. BUT, because D lacks references (which I'll never be able to understand), functions can't return l-values. Expressions can be treated as so though. One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value. Expressions can also be used for simpler operations, such as dereferencing an array or pointer, to make code easier to read and write: int* ptr = new int; expression exp = *ptr; exp = 62; int[] arr = [15, 30, 45, 60]; expression val = arr[2]; val = 30; Expressions can also handle constants, so it could be used as an alias which can take arguments. (Maybe require const property?) const expression ptr(T) = T*; -- Expressions are NOT mixins! You can't use undeclared symbols in an expression. -- Expressions are NOT functions! The expression is inlined into the code.votes++ This is a good idea and could really complement mixins in a nice way. What should the limits of this syntax be? should this be allowed. //declared as expression loop(a,b) = foreach(i,s,a)b[i]=s; //used as char[] foo=new char[10]; char[] bar=new char[10]; loop(foo,bar); to be able to do this would be intresting as it could allow for some powerfull syntax abstractions. Another intresting case would be. //declared as this expression mix = AMixin!(bool)(5); //used like this mix; here mix would be a mixin doing some work rather than declaring variables and I would expect that the expression have there own scope so that no mixin variables is leaked into the surrounding namespace.
Jan 31 2007
Johan Granberg Wrote:votes++ This is a good idea and could really complement mixins in a nice way. What should the limits of this syntax be? should this be allowed. //declared as expression loop(a,b) = foreach(i,s,a)b[i]=s; //used as char[] foo=new char[10]; char[] bar=new char[10]; loop(foo,bar); to be able to do this would be intresting as it could allow for some powerfull syntax abstractions. Another intresting case would be. //declared as this expression mix = AMixin!(bool)(5); //used like this mix; here mix would be a mixin doing some work rather than declaring variables and I would expect that the expression have there own scope so that no mixin variables is leaked into the surrounding namespace.I think loops would be best left to functions. Just my opinion anyways, I think expressions should have a return value. If it contains a loop, then it has no return value and is nothing more than a command, aka function. void loop(Ta, Tb)(Ta[] a, Tb[] b){ foreach(i,s; a)b[i]=s; } // In the case of dynamic arrays, it will point to the same block of data, so no pointer is necessary.Walter had the idea of allowing an expression to bind to an alias, and to allow the following: template mul(alias a, alias b) { alias (a * b) mul; }The problem with aliases is that the end expression must be constant. Expressions are more like functions and can give non-constant values.This is something I've thought about myself. One possible solution though is lazy arguments. If you think about it, the expression given to a lazy argument is only evaluated once, and it remembers the value. The same could be possible for expressions. expression max(lazy a, lazy b) = a > b ? a : b; One topic I forgot to mention is using expressions as template arguments. This is the idea I had for it: template temp(expression a(a, b)){ } // Must have two arguments, a and b template temp(expression b){ } // Can accept any set of arguments expression mul(a, b) = a * b; temp!(mul); // Expression is given to template I think expressions should be like structs though; Even though the expression is exactly the same, it should be treated as a different type. expression mul1(a, b) = a * b; expression mul2(a, b) = a * b; is(mul1 == mul2) // False temp!(mul1) is different than temp!(mul2)One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value.Nah, that won't work. It will doubly evaluate one of its arguments. But the feature will allow very simple implementation of my varargs_reduce and varargs_reduce_parallel primitives, e.g.:
Feb 01 2007
Xinok wrote:Nonono. As I said (in the paragraph you quoted above), expressions could bind to aliases, so mul accepts any two expressions and creates an expression.Walter had the idea of allowing an expression to bind to an alias, and to allow the following: template mul(alias a, alias b) { alias (a * b) mul; }The problem with aliases is that the end expression must be constant. Expressions are more like functions and can give non-constant values.But this semantics is opposite to lazy's current semantics. Overall, I think the alias-based macros are more in keep with the current language and allow more expressive constructs beyond expression manipulation.This is something I've thought about myself. One possible solution though is lazy arguments. If you think about it, the expression given to a lazy argument is only evaluated once, and it remembers the value. The same could be possible for expressions. expression max(lazy a, lazy b) = a > b ? a : b;One example of a recent post, the max function: expression max(a, b) = a > b ? a : b; Can handle any and all types, and you can use it as an l-value.Nah, that won't work. It will doubly evaluate one of its arguments. But the feature will allow very simple implementation of my varargs_reduce and varargs_reduce_parallel primitives, e.g.:One topic I forgot to mention is using expressions as template arguments. This is the idea I had for it: template temp(expression a(a, b)){ } // Must have two arguments, a and b template temp(expression b){ } // Can accept any set of arguments expression mul(a, b) = a * b; temp!(mul); // Expression is given to templateYes. This is exactly Walter's idea: allow an expression to bind to an alias. No need for a new keyword. Andrei
Feb 01 2007
On Thu, 01 Feb 2007 08:08:48 +0200, Xinok <xnknet gmail.com> wrote:I've held off this idea for a little while now, but I think it could r=eally be a nice addition to D.Expressions are basically aliases which can accept arguments. Unlike a=liases though, an expression can accept non-const arguments.expression mul(a, b) =3D a * b; // Syntax is different from aliases, f=or good reason...int main(){ int a =3D 35, b =3D 62; a =3D mul(a, b); }votes++. However, why not go all the way and implement inline functions?= That is, functions which are inlined in the expression using them. These would have the following advantages: * using a simple syntax (an "inline" attribute for the function); * since no calls to the function are made, the compiler can decide whe= n to use lazy argument evaluation and when to cache the results; * since it's a function, it's possible to use complex instructions (bl= ocks, loops, etc.) * the code (arguments, return value) simply direct the data flow - the= re is no real stack frame, the return value is passed immediately and ev= aluated with the context, etc. Unless there is a strong barrier in the c= ompiler between compiling expressions and statements (so that compound s= tatements could be inlined in expressions), it shouldn't be hard to impl= ement either. In contrast to the above method, however, it won't be possible to use th= e same expression/inline function with different types. For example, usi= ng the syntax above, expression div(a, b) =3D a/b; int main(){ int i1 =3D 65, i2 =3D 32, i3; i3=3Ddiv(i1, i2); float f1 =3D 6.5, f2 =3D 3.2, f3; f3=3Ddiv(i1, i2); } won't be directly possible with inline functions - but is easily done by= enclosing the inline function in a template (not unlike regular functio= ns): template div(T) { inline T div(T a, T b) { return a/b; } } void main() { writefln(div!(int)(5,2)); writefln(div!(float)(5.5,2.1)); } What do you think? -- = Best regards, Vladimir mailto:thecybershadow gmail.com
Feb 02 2007
Vladimir Panteleev wrote:votes++. However, why not go all the way and implement inline functions? That is, functions which are inlined in the expression using them. These would have the following advantages: * using a simple syntax (an "inline" attribute for the function); * since no calls to the function are made, the compiler can decide when to use lazy argument evaluation and when to cache the results; * since it's a function, it's possible to use complex instructions (blocks, loops, etc.) * the code (arguments, return value) simply direct the data flow - there is no real stack frame, the return value is passed immediately and evaluated with the context, etc. Unless there is a strong barrier in the compiler between compiling expressions and statements (so that compound statements could be inlined in expressions), it shouldn't be hard to implement either. template div(T) { inline T div(T a, T b) { return a/b; } } void main() { writefln(div!(int)(5,2)); writefln(div!(float)(5.5,2.1)); } What do you think???? This has been part of the compiler for a very long time. There's no need for the inline keyword, just use the -inline compiler switch and it happens automatically. Or did I miss something?
Feb 02 2007
On Fri, 02 Feb 2007 12:57:06 +0200, Don Clugston <dac nospam.com.au> wrote:??? This has been part of the compiler for a very long time. There's no need for the inline keyword, just use the -inline compiler switch and it happens automatically. Or did I miss something?AFAIK, even with -inline, the compiler doesn't automatically inline functions in certain circumstances - for example, if they contain loops. Sometimes it is critical for the functions to be inlined - out of performance or security (obfuscation) reasons. Either way, what are the disadvantages of such inline functions compared to the discussed expression types? -- Best regards, Vladimir mailto:thecybershadow gmail.com
Feb 02 2007
Vladimir Panteleev wrote:On Fri, 02 Feb 2007 12:57:06 +0200, Don Clugston <dac nospam.com.au> wrote:They are different beasts. Expression transformations specify syntactic processing exclusively. Andrei??? This has been part of the compiler for a very long time. There's no need for the inline keyword, just use the -inline compiler switch and it happens automatically. Or did I miss something?AFAIK, even with -inline, the compiler doesn't automatically inline functions in certain circumstances - for example, if they contain loops. Sometimes it is critical for the functions to be inlined - out of performance or security (obfuscation) reasons. Either way, what are the disadvantages of such inline functions compared to the discussed expression types?
Feb 02 2007
Functions aren't powerful enough to be used in place of expressions. -- Functions can't return references, which prevents you from using them as l-values. The best you can do is return a pointer, which you have to manually dereference. -- All of the types in an expression are automatic, including the return type. Getting the parameter types for a function can be simple, but the return type can be difficult. -- Functions can't take full advantage of features like conditionals, because conditionals can have two or more possible 'return types'. Functions can only return a single type.
Feb 02 2007
Xinok wrote:Functions aren't powerful enough to be used in place of expressions. -- Functions can't return references, which prevents you from using them as l-values. The best you can do is return a pointer, which you have to manually dereference. -- All of the types in an expression are automatic, including the return type. Getting the parameter types for a function can be simple, but the return type can be difficult. -- Functions can't take full advantage of features like conditionals, because conditionals can have two or more possible 'return types'. Functions can only return a single type.Conditionals always have one return type that can be detected as typeof(true? T : U). The advantage of expression aliases is that indeed there's no need to worry for their type. Also, expression aliases work when passing e.g. constant expressions to templates because they expand during compilation. The disadvantages are that (1) writing statements in the replacement expression is not possible, (2) separate compilation is not possible. Andrei
Feb 02 2007
Andrei Alexandrescu (See Website For Email) wrote:Xinok wrote:That's already possible, albeit clumsy. You can duplicate the expression inside an anonymous delegate, and use D's automatic return type inference, together with an is( : return) expression. This works even when there are statements in the delegate, together with expressions. So I suspect something even more general might be possible. Also, expression aliases work when passing e.g.Functions aren't powerful enough to be used in place of expressions. -- Functions can't return references, which prevents you from using them as l-values. The best you can do is return a pointer, which you have to manually dereference. -- All of the types in an expression are automatic, including the return type. Getting the parameter types for a function can be simple, but the return type can be difficult. -- Functions can't take full advantage of features like conditionals, because conditionals can have two or more possible 'return types'. Functions can only return a single type.Conditionals always have one return type that can be detected as typeof(true? T : U). The advantage of expression aliases is that indeed there's no need to worry for their type.constant expressions to templates because they expand during compilation. The disadvantages are that (1) writing statements in the replacement expression is not possible, (2) separate compilation is not possible. Andrei
Feb 02 2007
Don Clugston wrote:Andrei Alexandrescu (See Website For Email) wrote:Oh, yes, it's definitely possible. "Worrying about" does not mean "can't do anything about" :o). It's just annoying. For example, half of varargs_reduce deals with types, although it doesn't care about them at all. A more general thing I was thinking of was to allow statements to bind to aliases. Then D will have a pretty interesting macro mechanism. AndreiXinok wrote:That's already possible, albeit clumsy. You can duplicate the expression inside an anonymous delegate, and use D's automatic return type inference, together with an is( : return) expression. This works even when there are statements in the delegate, together with expressions. So I suspect something even more general might be possible.Functions aren't powerful enough to be used in place of expressions. -- Functions can't return references, which prevents you from using them as l-values. The best you can do is return a pointer, which you have to manually dereference. -- All of the types in an expression are automatic, including the return type. Getting the parameter types for a function can be simple, but the return type can be difficult. -- Functions can't take full advantage of features like conditionals, because conditionals can have two or more possible 'return types'. Functions can only return a single type.Conditionals always have one return type that can be detected as typeof(true? T : U). The advantage of expression aliases is that indeed there's no need to worry for their type.
Feb 02 2007
Andrei Alexandrescu (See Website For Email) wrote:Don Clugston wrote:Agreed. I think mixins were supposed to be a replacement for macros, but they're just too limited.Andrei Alexandrescu (See Website For Email) wrote:Oh, yes, it's definitely possible. "Worrying about" does not mean "can't do anything about" :o). It's just annoying. For example, half of varargs_reduce deals with types, although it doesn't care about them at all. A more general thing I was thinking of was to allow statements to bind to aliases. Then D will have a pretty interesting macro mechanism.Xinok wrote:That's already possible, albeit clumsy. You can duplicate the expression inside an anonymous delegate, and use D's automatic return type inference, together with an is( : return) expression. This works even when there are statements in the delegate, together with expressions. So I suspect something even more general might be possible.Functions aren't powerful enough to be used in place of expressions. -- Functions can't return references, which prevents you from using them as l-values. The best you can do is return a pointer, which you have to manually dereference. -- All of the types in an expression are automatic, including the return type. Getting the parameter types for a function can be simple, but the return type can be difficult. -- Functions can't take full advantage of features like conditionals, because conditionals can have two or more possible 'return types'. Functions can only return a single type.Conditionals always have one return type that can be detected as typeof(true? T : U). The advantage of expression aliases is that indeed there's no need to worry for their type.Andrei
Feb 02 2007