Puzzled by this behavior

Don Allen donaldcallen at gmail.com
Tue May 31 22:48:55 UTC 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):
>
> ```d
> void foo() { bar(); }
> void bar() { writeln("hello"); }
> foo();
> ```
>
> would work with scheme rules but
>
> ```d
> 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:
>
> ```d
> 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




More information about the Digitalmars-d mailing list