Jump to page: 1 2
Thread overview
February 01, 2007
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.
February 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.

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
February 01, 2007
Andrei Alexandrescu (See Website for Email) wrote:
> 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

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.
February 01, 2007
Kyle Furlong wrote:
> Andrei Alexandrescu (See Website for Email) wrote:
>> 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
> 
> 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.

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.
February 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.

February 01, 2007
Don Clugston wrote:
> Kyle Furlong wrote:
>> Andrei Alexandrescu (See Website for Email) wrote:
>>> 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
>>
>> 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.
> 
> 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.

I suppose I meant easy use, not easy implementation. <g>
February 01, 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.


> > 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.:

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)
February 01, 2007
Xinok wrote:
>> 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.

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.

>>> 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.:
> 
> 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;

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.

> 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

Yes. This is exactly Walter's idea: allow an expression to bind to an alias. No need for a new keyword.


Andrei
February 02, 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 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);
> }

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.

In contrast to the above method, however, it won't be possible to use the same expression/inline function with different types. For example, using the syntax above,

expression div(a, b) = a/b;
int main(){
	int i1 = 65, i2 = 32, i3;
	i3=div(i1, i2);
	float f1 = 6.5, f2 = 3.2, f3;
	f3=div(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 functions):

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
February 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?
« First   ‹ Prev
1 2