Jump to page: 1 25  
Page
Thread overview
May 31

This

import std.stdio;

void foo() {
    bar();
}
void bar() {
    writeln("Hello world");
}
int main(string[] args)
{
    foo();
    return 0;
}

compiles and does what you'd expect, despite the forward reference from foo to bar, which, of course, would not work in C.

But

import std.stdio;

int main(string[] args)
{
    void foo() {
        bar();
    }
    void bar() {
        writeln("Hello world");
    }
    foo();
    return 0;
}

gives the error

(dmd-2.100.0)dca@pangloss.allen.net:/home/dca/Software/d_tests$ dmd test5.d
test5.d(6): Error: undefined identifier `bar`

This only works if you interchange the order of foo and bar, eliminating the
forward reference.

This strikes me as pretty inconsistent behavior, especially to someone who has written as much Scheme and Haskell as I have. I've also not found it documented, though I could have missed it (if someone could point me to where this is discussed, I'd appreciate it). My main purpose in sending this message is to understand whether this is a bug or a documented "feature". If the former, I will file a bug report.

/Don

May 31
On 5/31/22 10:41, Don Allen wrote:

> I've also not
> found it documented

This is by-design. One place I found is the following FAQ entry:

  https://dlang.org/articles/faq.html#nested_forward_references

Ali

May 31
On Tuesday, 31 May 2022 at 17:41:18 UTC, Don Allen wrote:
> This strikes me as pretty inconsistent behavior

Code in functions is actually executed in sequence. Nested functions aren't exactly code, but the same rule applies to them. Consider:

int a = 5;
a = 6;
int b = a;

What is b? Of course we know since it happens in sequence. Same rule applies with nested functions.

> I've also not found it documented, though I could have missed it (if someone could point me to where this is discussed, I'd

It has its own section on the function page:

https://dlang.org/spec/function.html#nested-declaration-order
May 31

On Tuesday, 31 May 2022 at 17:41:18 UTC, Don Allen wrote:

>

This strikes me as pretty inconsistent behavior, especially to someone who has written as much Scheme and Haskell as I have. I've also not found it documented, though I could have missed it (if someone could point me to where this is discussed, I'd appreciate it). My main purpose in sending this message is to understand whether this is a bug or a documented "feature". If the former, I will file a bug report.

This behavior (along with several workarounds) is documented in the spec here:

https://dlang.org/spec/function.html#nested-declaration-order

May 31
On Tue, May 31, 2022 at 05:41:18PM +0000, Don Allen via Digitalmars-d wrote:
> This
> 
> ````
> import std.stdio;
> 
> void foo() {
>     bar();
> }
> void bar() {
>     writeln("Hello world");
> }
> int main(string[] args)
> {
>     foo();
>     return 0;
> }
> ````
> 
> compiles and does what you'd expect, despite the forward reference from foo to bar, which, of course, would not work in C.
> 
> But
> ````
> import std.stdio;
> 
> int main(string[] args)
> {
>     void foo() {
>         bar();
>     }
>     void bar() {
>         writeln("Hello world");
>     }
>     foo();
>     return 0;
> }
> ````
> gives the error
> ````
> (dmd-2.100.0)dca@pangloss.allen.net:/home/dca/Software/d_tests$ dmd test5.d
> test5.d(6): Error: undefined identifier `bar`
> ````
> This only works if you interchange the order of foo and bar,
> eliminating the forward reference.
> 
> This strikes me as pretty inconsistent behavior, especially to someone who has written as much Scheme and Haskell as I have. I've also not found it documented, though I could have missed it (if someone could point me to where this is discussed, I'd appreciate it). My main purpose in sending this message is to understand whether this is a bug or a documented "feature". If the former, I will file a bug report.
[...]

This is expected.  In module scope, D tries as much as possible to eliminate forward reference issues.  (There are some buggy/inconsistent cases, but those are generally rare unless you're doing something unusual.)

Inside function scope, however, it was deemed impractical because of closure over local variables:

	void myfunc() {
		int x;
		void woohoo() {
			x++; // this is legal, closes over x
		}
		...
	}

Had out-of-order declaration been allowed, it would have been rather confusing:

	void myfunc() {
		void woohoo() {
			x++; // should this close over 'x' inside the 'if'?
		}

		if (someWeirdCondition) {
			int x;
		}
	}

This potentially becomes worse when imported symbols are involved.

Hence, it was decided that inside function scope you must declare all symbols before referring to them, unlike module scope.


T

-- 
LINUX = Lousy Interface for Nefarious Unix Xenophobes.
May 31

On Tuesday, 31 May 2022 at 17:41:18 UTC, Don Allen wrote:

>
import std.stdio;

int main(string[] args)
{
    void foo() {
        bar();
    }
    void bar() {
        writeln("Hello world");
    }
    foo();
    return 0;
}

This is because you did it in a function body.
Within a function body each declaration opens a scope implicitly.
which gets closed at the end of the parent scope.
This makes sure you don't use variables before they got initialized.

int f()
{
// {
  int x;
//   {
  int y = x;  // works
//     {
  int y2 = z;  // does not work because z not in scope
                // also the value of z isn't defined yet
//       {
  int z;
  // } } } }
}

function declarations within a function body follow the same rules as variable declarations.
namely a declaration cannot forward reference another in the same function body.

May 31
On Tuesday, 31 May 2022 at 17:52:35 UTC, Adam D Ruppe wrote:
> On Tuesday, 31 May 2022 at 17:41:18 UTC, Don Allen wrote:
>> This strikes me as pretty inconsistent behavior
>
> Code in functions is actually executed in sequence. Nested functions aren't exactly code, but the same rule applies to them. Consider:
>
> int a = 5;
> a = 6;
> int b = a;
>
> What is b? Of course we know since it happens in sequence. Same rule applies with nested functions.
>
>> I've also not found it documented, though I could have missed it (if someone could point me to where this is discussed, I'd
>
> It has its own section on the function page:
>
> https://dlang.org/spec/function.html#nested-declaration-order

Code in Scheme functions are also evaluated in sequence, but functions can be mutually recursive whether at top-level or not, so the mere fact of sequential evaluation is not the explanation. What appears to matter is when the location of called functions are resolved -- compile-time (and if so, what pass?) or run-time.  In Scheme and Haskell, it is (at least conceptually) at run-time. D appears to do it both ways, depending on the particular situation (top-level? nested? static methods?). Without understanding the reason for this choice, I must say I find it quite odd.
May 31

On Tuesday, 31 May 2022 at 18:30:12 UTC, Don Allen wrote:

>

static methods?). Without understanding the reason for this choice, I must say I find it quite odd.

The reason is that D is more evolved than designed. There is no reason for mixing declaration order with checking for initialization before use.

This works with lambdas:

void main()
{
    int i;
    void delegate() foo;

    auto bar = (){
        writeln("Hello world");
        if(++i < 10) foo();
    };

    foo = (){ bar(); };

    foo();
}
May 31

On 5/31/22 2:30 PM, Don Allen wrote:

>

On Tuesday, 31 May 2022 at 17:52:35 UTC, Adam D Ruppe wrote:

>

On Tuesday, 31 May 2022 at 17:41:18 UTC, Don Allen wrote:

>

This strikes me as pretty inconsistent behavior

Code in functions is actually executed in sequence. Nested functions aren't exactly code, but the same rule applies to them. Consider:

int a = 5;
a = 6;
int b = a;

What is b? Of course we know since it happens in sequence. Same rule applies with nested functions.

>

I've also not found it documented, though I could have missed it (if someone could point me to where this is discussed, I'd

It has its own section on the function page:

https://dlang.org/spec/function.html#nested-declaration-order

Code in Scheme functions are also evaluated in sequence, but functions can be mutually recursive whether at top-level or not, so the mere fact of sequential evaluation is not the explanation.

The scheme code likely evaluates the definition of the function, without resolving what code to call until it encounters a call.

In other words, something like this in D (I haven't used scheme in a while, so we are going to use D syntax):

void foo() { bar(); }
void bar() { writeln("hello"); }
foo();

would work with scheme rules but

void foo() { bar(); }
foo();
void bar() { writeln("hello"); }

would not.

>

What appears to matter is when the location of called functions are resolved -- compile-time

It's not a matter of when they are resolved. D is fully capable of resolving functions at compile time out of order. It's a question of what symbols are in scope.

Consider:

void foo() { writeln("outer foo"); }


void main()
{
   void bar() { foo(); }
   void foo() { writeln("inner foo"); }
   bar();
}

what should print is "outer foo", because that is what foo means at the point in which bar is being compiled. Inside functions, order of declaration is important and significant. Outside functions, they can be in any order, but must not be ambiguous. These are incompatible sets of rules. You have to pick one, and D chose to pick C rules inside functions (likely for compatibility), but allowed out of order declarations outside them because prototyping is just monotonous.

This is done on purpose. The easiest way to solve this is to declare the functions inside a struct in the function (as the workarounds suggest). I've done this many times when I have a significantly complex recursive algorithm that I don't want to expose outside the function.

Note that you can declare a prototype, but this also declares a symbol, and D does not allow you to redefine symbols.

-Steve

May 31

On Tuesday, 31 May 2022 at 19:59:21 UTC, Steven Schveighoffer wrote:

>

Note that you can declare a prototype, but this also declares a symbol, and D does not allow you to redefine symbols.

And this doesn't make any sense. Why would local functions not work like lambdas?

« First   ‹ Prev
1 2 3 4 5