May 31, 2022

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

>

This is done on purpose.

What's the purpose? It looks like just another atavism, reproducing itself without any purpose.

May 31, 2022

On 5/31/22 4:11 PM, Ola Fosheim Grøstad wrote:

>

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?

Because they aren't lambdas. To use your example:

void main()
{
    int i;
    void foo(); // declares foo with no definition

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

    void foo() {bar(); }; // redeclares foo, not allowed

    foo();
}

would be the same with lambdas as:

void main()
{
    int i;
    void delegate() foo; // declares foo and assigns it to null

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

    //foo = (){ bar(); }; // this is NOT a declaration, it's an assignment
    void delegate() foo = () {bar(); }; // redeclares foo, not allowed

    foo();
}

You can't "assign" functions like you can lambdas. And D does not allow redeclaring a symbol of any type in a specific scope.

-Steve

May 31, 2022

On 5/31/22 4:13 PM, Max Samukha wrote:

>

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

>

This is done on purpose.

What's the purpose? It looks like just another atavism, reproducing itself without any purpose.

To make code that is ported from C compile the same as it does in C.

-Steve

May 31, 2022

On Tuesday, 31 May 2022 at 20:24:17 UTC, Steven Schveighoffer wrote:

>
    void foo() {bar(); }; // redeclares foo, not allowed

This ought to define what has already been declared. That is the purpose of a prototype. Calling this a "redeclaration" is arbitrary.

>

would be the same with lambdas as:

    void delegate() foo; // declares foo and assigns it to null

This declares and defines foo.

>

You can't "assign" functions like you can lambdas. And D does not allow redeclaring a symbol of any type in a specific scope.

Why do you call it "redeclaring" the signature is the same!? That's just an after-the-fact explanation. Calling this redeclaring doesn't follow from how prototypes in C works. If the signature is the same then you can do it as many times as you want.

May 31, 2022

On 5/31/22 4:33 PM, Ola Fosheim Grøstad wrote:

>

On Tuesday, 31 May 2022 at 20:24:17 UTC, Steven Schveighoffer wrote:

>
    void foo() {bar(); }; // redeclares foo, not allowed

This ought to define what has already been declared. That is the purpose of a prototype. Calling this a "redeclaration" is arbitrary.

I don't know why it does that, I assume Walter has a good reason for that. But they do work at module level (which is kinda weird, since you don't need them there). I had thought they weren't allowed.

> >

You can't "assign" functions like you can lambdas. And D does not allow redeclaring a symbol of any type in a specific scope.

Why do you call it "redeclaring" the signature is the same!? That's just an after-the-fact explanation. Calling this redeclaring doesn't follow from how prototypes in C works. If the signature is the same then you can do it as many times as you want.

I can't do:

int x;
int x = 5;

So I assume it's similar to declaring functions, but I don't know the reason it allows this for function prototypes at the module level.

-Steve

May 31, 2022

On Tuesday, 31 May 2022 at 20:47:36 UTC, Steven Schveighoffer wrote:

>

So I assume it's similar to declaring functions, but I don't know the reason it allows this for function prototypes at the module level.

Maybe he didn't think about mutual recursion.

Anyway, this is legal in C++:

extern int x;
extern int x;
void f();
void f();
void f(){ x=0;}
int x = 1;
May 31, 2022

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

>

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.

Because D has chosen to define its version of lexical scoping in this way in this case, but not other cases. What you say would not be controversial if we were talking about

     int bar = foo + 1;
     int foo = 0;

That doesn't work in D, Scheme or Haskell, nor should it, because the value of a variable not yet defined is required in the course of sequential evaluation of the statements above.

But in the situation I encountered, the sequential evaluation is of function definitions. The forward reference is within one of those definitions and the actual reference does not occur until the function is invoked, at which point both functions have been defined and therefore I would expect foo and bar to be available to each other. What D is doing here makes no sense to me and is particularly bizarre because it does the opposite at top level and inside structs.

>

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.

Well, I would argue that that kind of thinking leads to a language that is a collection of special cases, rather than a language built on a core set of principles consistently applied.

Ok. I've had my say. I think this is a mistake, but I don't expect it to change because of my objection, so no point in pursuing.

>

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.

My concern is not "solving this". My concern is whether the language is clean and consistent so I can have mental model of it I can rely upon, rather than constantly searching through documentation to learn how a particular special case is handled. I also care a lot about writing readable code, and about being able to limit the visibility of variables to just the scope that needs them and no more. The workarounds you cite frankly feel like hacks to me, ugly ways of working around problems in the language design.

/Don

>

-Steve

June 01, 2022
On 31.05.22 19:41, Don Allen wrote:
> 
> 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

Not sure if it's necessarily "inconsistent", but it is quite annoying and not really necessary. I have also complained about this before.

The simplest workaround is to make `foo` a template:

```d
import std.stdio;

int main(string[] args){
    void foo()(){
        bar();
    }
    void bar(){
        writeln("Hello world");
    }
    foo();
    return 0;
}
```
June 01, 2022
On 31.05.22 22:24, Steven Schveighoffer wrote:
> On 5/31/22 4:13 PM, Max Samukha wrote:
>> On Tuesday, 31 May 2022 at 19:59:21 UTC, Steven Schveighoffer wrote:
>>
>>> This is done on purpose.
>>
>> What's the purpose? It looks like just another atavism, reproducing itself without any purpose.
> 
> To make code that is ported from C compile the same as it does in C.
> 
> -Steve

There are local functions in C?
May 31, 2022

On 5/31/22 8:17 PM, Timon Gehr wrote:

>

On 31.05.22 22:24, Steven Schveighoffer wrote:

>

On 5/31/22 4:13 PM, Max Samukha wrote:

>

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

>

This is done on purpose.

What's the purpose? It looks like just another atavism, reproducing itself without any purpose.

To make code that is ported from C compile the same as it does in C.

There are local functions in C?

I mean the lookup mechanisms are the same.

But yeah, there are no local functions in C. I just figured that this is so the compiler doesn't have to have weird special cases for lookups.

However, thinking about it more, we do allow function prototypes as local functions. But I can't figure out a way to actually define them, aside from using pragma(mangle).

I think there's an opportunity here where we can allow function prototypes, allow definitions later, and not break existing code.

-Steve