www.digitalmars.com         C & C++   DMDScript  

D - Ideas, thoughts, and criticisms, part two. About functions.

reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari cc.hut.fi> writes:
Ideas, thoughts, and criticisms (part 2)
----------------------------------------

(The topic says functions. Today's rant is actually about a bit more
than that: multiple-value expressions, the unification of expressions
and statements, and a sidenote about how to implement generics like I
would do it. (The C++ way that is. *g*))

Here we go again.

I thought a bit about functions.

Let's start from an example.

http://www.digitalmars.com/d/function.html contains the following
example:

int foo(int x, out int y, inout int z, int q);

To me it just doesn't seem right. Something is wrong. Maybe it is the
words "inout" and "out" which just I feel like should not be there.
(sorry, all due respect to IDL but I don't use it, I don't like it, and
don't think it's worthwhile modeling a language to match IDL syntax.
Just my opinion. Just the two cents, or something.)

Probably it's just my view that there should not be _two_ places where a
function can return values - the "ordinary" return value and then the
"out" return value.  Could not the function return multiple values?

int, int foo(int x, inout int z, int q);

Now something is still wrong. I put the second argument to the function,
"int y" to the left of the function. Everything's fine except that we
can't see its name there. We cannot document the meanings of function's
return value, because we cannot give it a name.

But it has always been so. function's return value has indeed been
nameless. In C that is. But I don't see why it should be that way. Let's
give names to the return values of the function:

int val, int q
foo(int x, inout int z, int q);

Now that's pretty. We have documented our return values (they have
names), functions can return multiple values, and they are put in one
place. Now we need to see what we can do to the poor inout variable.
Maybe a reference ā la C++?

int val, int q
foo(int x, int& z, int q);

IMHO this syntax is nicer to read because there are no "inout" words
there, and the "in" and "out" values are grouped nicely.

This would also get rid of the little annoyance that in C++, if you want
a function that takes no arguments, you use just:

int f();        // function taking no arguments, returning int

but if you want to return no arguments, you have to use void:

void f(int);    // function taking int, returning nothing

Now, if you want to return nothing, just use:

f();

This return value thing has some other implications, too. It doesn't
come cheap. Grab a good hold on your chairs.

We need a new type expression altogether, let's call it a multivalued
expression. Like I said, I'm no grammar expert so I'm not going to
concentrate on that side. But it seems we'd have to scrap the
well-served comma expression. Let's see what we can figure out to
replace it later, and now concentrate on the multivalued function thing.

int val1, int val2 f();

g()
{
    // you could, of course, do this:
    int x, y;
    x, y = f();

    // and naturally this:
    x, y = y, x;

    // and maybe this:
    int a, b = f();

    // now if you would want to ignore one of the return values, what to
    // do then?
    x = f();    // only take the first one
    , y = f();  // only take the second one (but this does not fulfill my
                // aesthetic needs, so maybe not like this...)
    // so perhaps something else is needed: (this _could_ be interpreted
    // to assignment to an unnamed variable of type void - or just as a
    // syntactic sugar for "int dummy, y = f();"
    void, y = f();
}

While we're at it, why not exploit the naming of the return values a
bit. We could actually represent returning multiple values as returning
a struct. Suppose that we have two f's, so we need to specify one in
order to get a type out of it:

int   val1,   int val2 f(int x)     { return x, x*2;    }
float val1, float val2 f(float x)   { return x, x*2.0f; }

// assuming that the form f() is unevaluated, and only its type is
// requested via the .type property:
// for practical purposes, f_int_return_type is a struct { int val1; int val2; }
// f(int) is there just for disambiguation purposes.
alias f(int).type f_int_return_type;

g()
{
    f_int_return_type ret = f(5);
    assert(ret.val2 == 10);
}

With this, and the "with" expression (or even without this, but with
"with" expression and some sweet syntactic sugar) we could also do

g()
{
    with (f(5))
    {
        do_whatever_with(val1, val2);
    }
}

Now, there is just no end for the syntactic sugar if that's the way to
go. Now wouldn't it taste good if you could do the following:

int, int f(int x) {           // of course, you don't need to name them if you
    return x, x+1;            // don't want to. I just didn't bother to
}                             // invent names right now

g(int first, int second, int third)
{
    printf("%d, %d, %d\n", first, second, third);
}

main()
{
    g(1, f(2));     // will print "5, 6, 7"
}

Now how does this sound like?

And of course, we wouldn't have to specify return parameters at a first
"return" line if we didn't want to. It could be done as they do in
pascal (I'm not one of them, mind you, but anyway I could imagine this
style of returning values being easier to optimize or whatever. I'm no
premature optimizer either. No way, not me. *g*).

For example, suppose that we have a function that calculates a sine and
a cosine. That's not really out of this world, x87 math coprosessor has
one. It's called fsincos (see intel:ia32_vol1) and it produces a sine
and cosine of an argument faster than a fsin and fcos in succession. So,
if we would like to commit a little sin (no pun intended) and
premature-optimize a bit:

module intrinsic; // or whatever

float sin_ret,
float cos_ret
inline sincos(float argument)
{
    asm
    {
        // now, here we assume that the compiler can handle putting
        // argument and the sine and cosine in the right places...
        // I might wrong in my assembly, I just learned it yesterday.
        // This is just an example.
        fsincos argument; 
        mov     cos_ret, st(1)
        mov     sin_ret, st(0)
    }
}

Or, for a more common example, everyone probably knows that a common DIV
instruction on IA-32 architecture (intel:ia32_vol2) produces not only the
quotient but also the remainder as a side effect; so we could actually
make 

int quotient, int remainder inline op.div(int dividee, int divisor)
{
    // assembly code to DIV dividee by divisor,
    // and move EAX to quotient (which probably is EAX anyway)
    // and move EDX to remainder.
    // If one of the return values turns out not to be used,
    // compiler is smart enough to forget unneeded MOV. EDX/EAX 
}

and then we could have (since modulo is in no way special)
int remainder, int quotient inline op.mod(int dividee, int divisor)
{
	quotient, remainder = dividee, divisor; // will call op.div()
}
Oh yeah, I forgot what to do with the poor comma operator.

My idea would be to simply do away with it and replace it with the block
expression, so that

expression1, expression2;
would yield expression1 in a single-valued context
(and a (expression1, expression2) in a multivalued context)

and

{ expression1;
  expression2; }

would yield expression2, and that would be more general anyway.

Of course, that would like a serious rearrangement of things.
Expression would be statement, and a statement would be expression.
And everything would have to be functional, not just half-functional as
it is now.

Besides, I like the idea of functional-style "if" where if is an
expression, not a statement:

// the return value need not be named, since the name of the function
// documents it clearly enough
int max(int lhs, int rhs)
{
     return
        if (lhs < rhs)
            rhs;
        else
            lhs;
}

Of course, that can be taken further and make the last
statement/expression of the function to yield the return value:

int max(int lhs, int lhs)
{
    // if yields one of its statements
    if (lhs < rhs)
        rhs;
    else
        lhs;
}

(This of course renders needless the weird trophy of the last century,
the infamous :? operator, which is a bit annoying to use repeatedly.
You have possibly seen this, and run away from it as fast as you could:

    int x = test1() ? something
                    : test2() ? somethingElse
                              : testEvenMore() ? yetanotherthing
                                               : lastTest() ? awwintired
                                                            : phew;
)

If you went so far as to make tail recursion optimization a feature of
the language (as in scheme), we are sure to see code like:

// recursive helper function
private int inline max(int[] array, int maxSoFar, uint idx)
{
    if (idx == array.length)
        maxSoFar;
    else
        max(array, max(array[i], maxSoFar), idx + 1);

}

// the main entrance to the helper function.
// return maximum entry in the array.
// in case of empty array, return int.min
int inline max(int[] array)
{
    return max(array, int.min, 0);
}

Quelle elegance! Well, that's a question of opinion but this kind of
stuff _would_ make it easier to use functional-style elements in D and
make it even more multi-paradigm than C++ is. (For the good or the
worse.)

Now while we're at it, and we've invented multivalued expressions
(tuples, if you like that name better) on the way, let's go generic. But
let's not go totally generic quite yet. Only generic when it comes to
the argument values. See, let's first introduce a new keyword, "rest",
our friend on the way to the general handling of the parameters:

int inline sum()
{
    return 0;
}

// calculate the sum of its arguments.
int inline sum(int head, rest tail)
{
    // head is the first argument,
    // and "tail", formed with the help of the "rest" keyword,
    // is the tuple containing the rest of them. tail may be
    // empty, which is as good as "void".
    // Of course, you'd better inline this function.
    return head + sum(tail);
}

Now what happens when the compiler starts to generate the code for, for
example, sum(1, 2, 3), is:

sum(1, 2, 3)
-> sum(head = 1, tail = (2, 3))
   -> 1
    + sum(2, 3)
      -> 2
       + sum(3)
         -> 3
          + sum()
            -> 0
-> 1 + 2 + 3
-> 6

And then it stops. If the user had provided something like (sum(1,x,2))
in between, it would've generated the code to calculate 1 + x + 2. Et
cetera. Of course, there might be a way to do stuff like this in
run-time too - but it just might not be needed because of arrays. But
let's see something that arrays are not able to do. To the generics.

I'd really really really very much like to have a generic function
syntax which I could use like I can in C++. Only if it can be fitted
into the existing D language, of course. 

Like as follows:

print() { }

print(int i)    {    printf("%d", i);    }
print(float f)  {    printf("%f", f);    }
print(String s) {  /* print a string */  }
print(...)      { /* you get the point*/ }

// note we don't need a template parameter for tail since it's
// implicitly matched to be the right type
template<type T>
inline print(T head, rest tail)
{
    print(head);
    print(tail);
}

Then you could do, type-safely,

print("value of i is = ", i, "\n", /* as many arguments as you like */);

and the compiler will generate you nice code for that. Inline-only. Of
course.

Of course, it goes without mentioning that C++ did this ages ago with
its << syntax, which is IMHO kind of a cool thing but it turns out some
people don't like it. Maybe this syntax is friendlier. It can probably
be made better. Give me your best shots.

And please apply the same C++-style generic function syntax for to the
examples above - you probably did - and see how it shines in proportion
to any other generic syntax ever invented. It would be very nice to see
that in D, too.

Oh yeah, let me warn you (regarding multiple return values), functions
return multiple return values might (or might now) also mess with
function pointer syntax and/or overloading issues.  (I'm thinking now
that since a i32 (*f)() must pass just one parameter, a i32, i32 (*f)()
could not be be used as one because it might clobber stack or registers
or whatever.  But on the other hand, if it uses an unused part of the
stack, what the hell - just leave the caller the first value and leave
to the programmer the problem that he has a function which does unused
computation.)

Say what you think.

Antti.

References:

(intel:ia32_vol1)
http://www.intel.com/design/pentium4/manuals/245470.htm

(intel:ia32_vol2)
http://www.intel.com/design/pentium4/manuals/245471.htm
Aug 26 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Tue=2C 27 Aug 2002 02=3A52=3A42 +0000 =28UTC=29 Antti Syk=5Fri
=3Cjsykari=40cc=2Ehut=2Efi=3E wrote=3A

=3E int val=2C int q
=3E foo=28int x=2C inout int z=2C int q=29=3B

In other words=2C you want tuples=2E A nice feature=2E But out parameters are a
bit 
more
than that - don't forget that you can overload functions based on types of their
parameters=2C and not by return type! For example=2C in stream=2Ed=2C I wrote=3A

=09void read=28out byte x=29=3B
=09void read=28out short x=29=3B
=09void read=28out int x=29=3B
=09=2E=2E=2E

And then you can write=3A

=09int n=3B
=09file=2Eread=28n=29=3B

And correct version will be called=2E
 
=3E Maybe a reference =E0 la C++=3F
=3E 
=3E int val=2C int q
=3E foo=28int x=2C int& z=2C int q=29=3B
=3E 
=3E IMHO this syntax is nicer to read because there are no =22inout=22 words
=3E there=2C and the =22in=22 and =22out=22 values are grouped nicely=2E

I still like =22out=22 more=2E 
 
=3E Now=2C if you want to return nothing=2C just use=3A
=3E 
=3E f=28=29=3B

This makes it harder to parse=2E Also=2C when I look at it=2C my mind sees a 
function call=2C and
not a declaration=2E =22void=22 can be treated as =22procedure=22 in other
languages=2C so 
I don't
really see a problem here=2E
 
=3E We need a new type expression altogether=2C let's call it a multivalued
=3E expression=2E Like I said=2C I'm no grammar expert so I'm not going to
=3E concentrate on that side=2E But it seems we'd have to scrap the
=3E well-served comma expression=2E Let's see what we can figure out to
=3E replace it later=2C and now concentrate on the multivalued function thing=2E

It is called a tuple=2E 

 
=3E If you went so far as to make tail recursion optimization a feature of
=3E the language =28as in scheme=29=2C we are sure to see code like=3A
=3E 
=3E =2F=2F recursive helper function
=3E private int inline max=28int=5B=5D array=2C int maxSoFar=2C uint idx=29
=3E {
=3E     if =28idx =3D=3D array=2Elength=29
=3E         maxSoFar=3B
=3E     else
=3E         max=28array=2C max=28array=5Bi=5D=2C maxSoFar=29=2C idx + 1=29=3B
=3E 
=3E }

Yes=2C and then D will become a functional language=2E=2E=2E
No=2C thanks! =3D=29
 
Aug 27 2002
prev sibling next sibling parent "Sean L. Palmer" <seanpalmer earthlink.net> writes:
I think I, for one, am with you on all these issues.

Sean

"Antti Sykäri" <jsykari cc.hut.fi> wrote in message
news:akephp$sbc$1 digitaldaemon.com...
 Ideas, thoughts, and criticisms (part 2)
 ----------------------------------------

 (The topic says functions. Today's rant is actually about a bit more
 than that: multiple-value expressions, the unification of expressions
 and statements, and a sidenote about how to implement generics like I
 would do it. (The C++ way that is. *g*))
Aug 27 2002
prev sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari cc.hut.fi> writes:
In D, Pavel Minayev <evilone omen.ru> wrote:
 But out parameters are a bit more than that - don't forget that you
 can overload functions based on types of their parameters, and not by
 return type! For example, in stream.d, I wrote:
 
void read(out byte x);
void read(out short x);
void read(out int x);
That looks nice, and might be the right way to solve the problem (which problem, that I will address shortly). But, as declaring void read(out byte x); is practically the same as declaring byte read(); - the difference is merely syntactical - then why not extend the idea of overloading functions to also cover the case of overloading them by the return type? The intention of the above-mention code would not change if it would be written: byte read(); short read(); int read(); void f() { int x; x = read(); } Only it would mean that the type of return value of "read()", and therefore the function to be called, would have to be determined by analyzing its context - which might have unexpected consequences. Think about that. Also, "out" parameters make it impossible to ignore a return value, since we need a typed lvalue parameter as an argument to determine which overloaded function to call. That might, or might not, be a good idea. The core of the problem at hand is ultimately that we want to avoid redundancy in the code. Consider (C example): byte read_byte(); short read_short(); void redundancy_problem() { byte b; b = read_byte(); } Now assume you want to change the byte to short. You have to change the function call into a short, too. That's not fun, especially if the function is a long one and you can't see all references. Besides i So, in a modern language, we have to get rid of this kind of behavior. Well, in C++ we can do that: void read(byte& b); void read(short& b); void redundancy_solution() { byte b; short s; read(b); // calls read(byte&) read(s); // calls read(short&) } So far, so good. But we can do that also if we had overloading by return value types, or overloading by "out" types. (Which, the syntax set aside, are practically the same. Except you can ignore the return value(s).) void solution_by_overloading_return_values() { byte b; b = read(); // byte read() is called } void solution_by_overloading_out_value() { byte b; read(b); // read(out byte b) is called } Finally let's see two interesting alternatives to achieve the non-redundancy: 1. Type inference (notice how I'm trying to smuggle features commonly seen in functional languages into D? No, I'm really not. This is just an example. :-): void type_inference_example() { b; // type of b will be inferred soon enough... but it is not // too visible from the source code and seem like trickery b = read_byte(); b = read_short(); // error: cannot infer common supertype for b // .. or actually, if short _is_ considered // a supertype for b, we actually can! } 2. Or, we could have the "C++ with typeof" solution: void wicked_cplusplus_like_example() { byte b; b = read<typeof(b)>(); // yechh } No comments on that one. Actually, type inference might be nice; consider class Base { } class Derived : Base { } class AnotherDerived : Base { } class Foobar { } void type_inference_example_on_steroids(char[][2] obj_type) { // no type presented, leave it to the compiler to decide object0; object1; // these two cause object0 to have type Base switch (object_to_make[0]) { case "derived": object0 = new Derived(); break; case "anotherderived": object0 = new anotherDerived(); break; } // and these two cause object1 to be of type Object switch (object_to_make[1]) { case "derived": object1 = new Derived; case "foobar": object1 = new Foobar; // uh-oh... not much in common } } Actually, type inference might not be nice. At least not for me, since I want to know the type of the declared variable, and I want it to read right there in the code. I don't mind if, for example, a syntax-directed editor does the work for me (although it would have to me more like a semantics-directed editor): when I for example change the Foobar there to be Derived, it could inform me that "Your Object object1 now doesn't need to be Object any more, it should be Derived, so I made the change for you" or something like that. But anyway, this kind of work wouldn't be suitable to be done behind the curtains.
=3E Now, if you want to return nothing, just use:
=3E 
=3E f();
 
 This makes it harder to parse. Also, when I look at it, my mind sees a 
 function call, and not a declaration.
Was this ("harder to parse") the original reason why the "void" asymmetry (requiring void in return values but not arguments) wasn't dumped in C++? Can't remember, don't have a copy of "The Design and Evolution of C++" at hand, so can't check either. Stroustrup is good at defending the choices he made when designing C++. (There actually are a bunch of principles buried under it. *g*) Still: In my opinion, too many language features are crippled by the seemingly well-meaning pursuit of trying to make the language easy to parse. Consider the template syntax of C++: you have to say, for example, set<vector<char> > instead of the _far_ more natural set<vector<char>>, because someone decided that we must build lexical analyzers upon the glorious principle of always returning the longest possible match. Rat's ass I think. I think that lot of C++ code would simply look a lot better if, in the design phase, it had been decided to recognize ">>" as two ">" tokens if we're inside <> brackets. And I think that nobody's code would be broken even if we started to adhere to the new rule right now. How often do you use ">>" inside a template argument list? Theoretically possible that you'd want to do something like "const int n; set< vec < float, 32>>n > > funny;". But, hey, dream on. Besides you could circumvent that by defining a new constant before the set<float<>> usage, and it'd be much clearer. So, Including a rather simple special case in the lexer would've made the language look and feel better. Imagine! (It would've been simple -- as simple as making the /* */ comments nested, actually. A language, to be successful, must be cool.) And, in my opinion, look and feel of the language matter much more than the ease of parsing. Sometimes the two goals do not conflict. In fact, most of the time they don't - which deceitfully makes people associate a "easily readable language" with "easily parsable language". But when they do conflict, I'd opt for the better look and feel since the language has far more users than it has implementers. (And of course, function calls don't happen in the same places as function declarations. (At least as long as you cannot declare functions inside functions.) So it might not be that big an issue.) Finally, well, look and feel are a matter of opinion. But they are also always a fresh topic for a debate, since successful opinions are usually those that change :-)
"void" can be treated as "procedure" in other languages, so I don't
 really see a problem here.
By the way, something I really don't understand is that some languages actually have different keywords for procedures (which just execute code) and functions (which return a value). It's just silly. Maybe what I'm looking for here is a uniform interface - something that could be (ab)used in metaprograms. Something along these lines: First, a function f must have has the following parameters: f.returnTuple - a tuple type of values it returns f.argTuple - a tuple type of values it gets as an argument // please ignore the invented ad-hoc syntax and concentrate on the idea: template<function F> F.returnTuple printingFunctionWrapper( F fun, F.argTuple arguments) // this could be replaced with the "rest" { // keyword proposed earlier print("I'm calling function ", F.name, " with arguments "); printTuple(arguments); // do the actual function call, store return values F.returnValues retVals = fun(arguments); print(F.name, " returned: "); printTuple(retVals); return retVals; } As it might be obvious, this function is a wrapper that emulates normal functions but logs their arguments and return values. printTuple is a meta function which generates code which prints its arguments, their names and their types. For example: int squared_x square(int x) = { return x*x; } main() { printingFunctionWrapper(square, 5); } says: I'm calling function square with arguments int x = 5 square returned: int squared_x = 25 Now, I admit that implementing this and refining the idea might not be an easy task...
=3E private int inline max(int[] array, int maxSoFar, uint idx)
=3E {
=3E     if (idx == array.length)
=3E         maxSoFar;
=3E     else
=3E         max(array, max(array[i], maxSoFar), idx + 1);
 
 Yes, and then D will become a functional language...
 No, thanks! =)
Well, what I'm proposing here might not look like the traditional C syntax - but on the semantic level it would be exactly the same as the equivalent C code which 1) creates a nameless temporary value; 2) generates code which assigns to the temporary value; 3) returns (or yields) the temporary value at the end. (I think that the functional form might be easier to optimize. I can't say - haven't written an optimizing compiler yet.) Another nice-to-have functional-like thing would be - lambda functions (there is a need for it in some form in any language, even if would be implemented only as syntactical sugar for introducing a nameless global function and yielding its address) Let expressions or similar, on the other hand, wouldn't be needed. Already C has a form of let expressions; the following block structure is equivalent to (let ((x 5) (y 6)) (do_something)): { int x = 5; int y = 6; do_something(); } Antti. (By the way, Pavel, I found it necessary to do some search & replace on the quoted text, since my newsreader (slrn) shows your articles like this, apparently not recognizing the character set and escaping each punctuation mark:
=09void read=28out short x=29=3B
=09void read=28out int x=29=3B
=09=2E=2E=2E
Not nice :( If anyone knows a cure for this, I'd appreciate that.)
Aug 27 2002
parent reply Pavel Minayev <evilone omen.ru> writes:
On Tue=2C 27 Aug 2002 21=3A41=3A59 +0000 =28UTC=29 Antti Syk=5Fri
=3Cjsykari=40cc=2Ehut=2Efi=3E wrote=3A

=3E In D=2C Pavel Minayev =3Cevilone=40omen=2Eru=3E wrote=3A
=3E But=2C as declaring
=3E 
=3E void read=28out byte x=29=3B
=3E 
=3E is practically the same as declaring
=3E 
=3E byte read=28=29=3B

It's not! read further=2E=2E=2E
 
=3E - the difference is merely syntactical - then why not extend the idea of
=3E overloading functions to also cover the case of overloading them by the
=3E return type=3F The intention of the above-mention code would not change if
=3E it would be written=3A
=3E 
=3E byte read=28=29=3B
=3E short read=28=29=3B
=3E int read=28=29=3B
=3E 
=3E void f=28=29
=3E {
=3E     int x=3B
=3E     x =3D read=28=29=3B
=3E }
=3E 
=3E Only it would mean that the type of return value of =22read=28=29=22=2C and
=3E therefore the function to be called=2C would have to be determined by
=3E analyzing its context - which might have unexpected consequences=2E Think
=3E about that=2E

=22Context=22 is exactly the word=2E For example=2C one could write=3A

=09*=28*=28foo=28x=2C y=29=2Ebar=5Bz=5D=29=5Bu=5D=29 =3D read=28=29=3B

The compiler would have quite a lot of problems trying to figure out the 
appropriate
version of read=28=29=2E It gets even more complicated with things like
overloaded 
functions
and operators=2E=2E=2E

=3E Also=2C =22out=22 parameters make it impossible to ignore a return value=2C
=3E since we need a typed lvalue parameter as an argument to determine which
=3E overloaded function to call=2E That might=2C or might not=2C be a good
idea=2E

Well=2C you cannot ignore the result if you overload by return type either=2C 
since then it would
be unclear to the compiler what you're trying to call=3A

=09byte read=28=29=3B
=09int read=28=29=3B

=09read=28=29=3B=09=2F=2F which version is called=3F

You could probably provide a version returning void in such cases=2C but so can 
you do
with out-parameters=3A

=09void read=28byte=29=3B
=09void read=28int=29=3B
=09void read=28=29=3B

Now you can ignore the result=2E =09

=3E Was this =28=22harder to parse=22=29 the original reason why the =22void=22
=3E asymmetry =28requiring void in return values but not arguments=29 wasn't
=3E dumped in C++=3F Can't remember=2C don't have a copy of =22The Design and
=3E Evolution of C++=22 at hand=2C so can't check either=2E Stroustrup is good
at
=3E defending the choices he made when designing C++=2E =28There actually are a
=3E bunch of principles buried under it=2E *g*=29
=3E 
=3E Still=3A
=3E 
=3E In my opinion=2C too many language features are crippled by the seemingly
=3E well-meaning pursuit of trying to make the language easy to parse=2E

Okay=2C so I don't care about the parser=2E I just find the =22void=2E=2E=2E=22
syntax more 
beautiful=2E
When I see it=2C it is clear to me that I see a function declaration=2E If it 
wasn't there=2C
I could think it is a call=2E
 
=3E =28And of course=2C function calls don't happen in the same places as
=3E function declarations=2E =28At least as long as you cannot declare functions
=3E inside functions=2E=29 So it might not be that big an issue=2E=29

It is=2E Just pick up some old big K&R C program and try to read through it=2E
It 
just
drives me mad!
 
=3E By the way=2C something I really don't understand is that some languages
=3E actually have different keywords for procedures =28which just execute
=3E code=29 and functions =28which return a value=29=2E It's just silly=2E

No=2C it isn't=2E It's =22academic=22=2E Whether it is good or not is a matter
of taste=2E 
Me personally=2C
I simply don't care=2E It works the same way everywhere=2C what else=3F =3D=29
 
=3E =28By the way=2C Pavel=2C I found it necessary to do some search & replace
on
=3E the quoted text=2C since my newsreader =28slrn=29 shows your articles like
=3E this=2C apparently not recognizing the character set and escaping each
=3E punctuation mark=3A

Okay=2C I never liked this newsreader anyhow=2E =3D=29 So=2C anyone knows a
decent free 
one=3F
Aug 27 2002
next sibling parent Mac Reiter <Mac_member pathlink.com> writes:
=3E =28By the way=2C Pavel=2C I found it necessary to do some search & replace
on
=3E the quoted text=2C since my newsreader =28slrn=29 shows your articles like
=3E this=2C apparently not recognizing the character set and escaping each
=3E punctuation mark=3A

Okay=2C I never liked this newsreader anyhow=2E =3D=29 So=2C anyone knows a
decent free 
one=3F
I have been very happy with the web interface, but that is only good for these newsgroups. Mac
Aug 28 2002
prev sibling next sibling parent Suporte Internet <suporte spica.mps.com.br> writes:
Pavel Minayev <evilone omen.ru> wrote:
 On Tue, 27 Aug 2002 21:41:59 +0000 (UTC) Antti Syk_ri <jsykari cc.hut.fi>
wrote:
 (By the way, Pavel, I found it necessary to do some search & replace on
 the quoted text, since my newsreader (slrn) shows your articles like
 this, apparently not recognizing the character set and escaping each
 punctuation mark:
Okay, I never liked this newsreader anyhow. =) So, anyone knows a decent free one?
I use xnews for windows and tin for linux.
Aug 28 2002
prev sibling parent reply Olaf Rogalsky <olaf.rogalsky theorie1.physik.uni-erlangen.de> writes:
Pavel Minayev wrote:
 "Context" is exactly the word. For example, one could write:
 
         *(*(foo(x, y).bar[z])[u]) = read();
 
 The compiler would have quite a lot of problems trying to figure out the
 appropriate version of read().
The compiler already has these problems: *(*(foo(x, y).bar[z])[u]) = 3.1415926; // implicit conversion to int, or not? I like the idea of overloading by return type. Multiple return values are in my eyes a concept, which borrows too much from functional languages. I have the feeling, that functional programming is the wrong paradigm for D. -- +----------------------------------------------------------------------+ I Dipl. Phys. Olaf Rogalsky Institut f. Theo. Physik I I I Tel.: 09131 8528440 Univ. Erlangen-Nuernberg I I Fax.: 09131 8528444 Staudtstrasse 7 B3 I I rogalsky theorie1.physik.uni-erlangen.de D-91058 Erlangen I +----------------------------------------------------------------------+
Aug 28 2002
parent reply Pavel Minayev <evilone omen.ru> writes:
On Wed, 28 Aug 2002 19:15:44 +0200 Olaf Rogalsky 
<olaf.rogalsky theorie1.physik.uni-erlangen.de> wrote:

 The compiler already has these problems:
 *(*(foo(x, y).bar[z])[u]) = 3.1415926; // implicit conversion to int, or not?
And does it matter here? Whether the conversion happens or not, it is all built-in stuff, no user-defined functions are called.
Aug 28 2002
parent reply Olaf Rogalsky <olaf.rogalsky theorie1.physik.uni-erlangen.de> writes:
Pavel Minayev wrote:

 The compiler already has these problems:
 *(*(foo(x, y).bar[z])[u]) = 3.1415926; // implicit conversion to int, or not?
And does it matter here? Whether the conversion happens or not, it is all built-in stuff, no user-defined functions are called.
internal or not, the compiler needs to know the type of the LHS in order to carry out correct type conversions. based on that knowlegde it wouln't be too hard to implement function overloading by return type. -- +----------------------------------------------------------------------+ I Dipl. Phys. Olaf Rogalsky Institut f. Theo. Physik I I I Tel.: 09131 8528440 Univ. Erlangen-Nuernberg I I Fax.: 09131 8528444 Staudtstrasse 7 B3 I I rogalsky theorie1.physik.uni-erlangen.de D-91058 Erlangen I +----------------------------------------------------------------------+
Aug 30 2002
parent reply C.R.Chafer <blackmarlin nospam.asean-mail.com> writes:
Olaf Rogalsky wrote:

 internal or not, the compiler needs to know the type of the LHS in order
 to carry out correct type conversions. based on that knowlegde it wouln't
 be too hard to implement function overloading by return type.
Yes, but there are several issues involved in resolving ambiguities. Suppose we had ... /* prototypes - in reality they would be full definitions */ int f1( int a ); /* #1 */ int f1( long a ); /* #2 */ int f2( long a ); /* #3 */ long f2( long a ); /* #4 */ /* code (in procedure) */ int b = f1( f2( 42 ) ); Now do we call ... f1#1 on f2#3 or f1#2 on f2#4 This problem is not insurmountable - we could resolve this with a cast (and D would probably flag an error rather than try to guess the correct format if the cast was ommited). My option is that overloading on return type is of limited utility at the present time - though may be a useful addition in Dv2. C 2002/8/30
Aug 30 2002
parent Olaf Rogalsky <olaf.rogalsky theorie1.physik.uni-erlangen.de> writes:
"C.R.Chafer" wrote:
 My option is that overloading on return type is of limited utility at the
 present time - though may be a useful addition in Dv2.
Yes, I was aware of this ambiguity. And yes, agreed, I can live without return type overloading. It just would be neat :-). -- +----------------------------------------------------------------------+ I Dipl. Phys. Olaf Rogalsky Institut f. Theo. Physik I I I Tel.: 09131 8528440 Univ. Erlangen-Nuernberg I I Fax.: 09131 8528444 Staudtstrasse 7 B3 I I rogalsky theorie1.physik.uni-erlangen.de D-91058 Erlangen I +----------------------------------------------------------------------+
Aug 30 2002