Jump to page: 1 2 3
Thread overview
Nested functions should be exempt from sequential visibility rules
Apr 03, 2012
Nick Sabalausky
Apr 03, 2012
James Miller
Apr 03, 2012
Nick Sabalausky
Apr 03, 2012
deadalnix
Apr 03, 2012
Don Clugston
Apr 03, 2012
Nick Sabalausky
Apr 03, 2012
Don Clugston
Apr 05, 2012
Walter Bright
Apr 03, 2012
Timon Gehr
Apr 03, 2012
Don Clugston
Apr 03, 2012
Michel Fortin
Apr 03, 2012
Timon Gehr
Apr 03, 2012
Don Clugston
Apr 03, 2012
Timon Gehr
Apr 03, 2012
Don Clugston
Apr 03, 2012
Timon Gehr
Apr 03, 2012
Nick Sabalausky
Apr 03, 2012
Don Clugston
Apr 03, 2012
Timon Gehr
Apr 03, 2012
Nick Sabalausky
Apr 03, 2012
Timon Gehr
Apr 03, 2012
Martin Nowak
Apr 04, 2012
Xinok
Apr 03, 2012
bearophile
Apr 04, 2012
Don Clugston
Apr 05, 2012
Walter Bright
Oct 11, 2012
stas
April 03, 2012
Regarding this:

http://d.puremagic.com/issues/show_bug.cgi?id=790

I submit that nested functions should be exempt from the usual sequential visibility rules. (Therefore, mutually recursive nested functions would become possible.)

Or at the very *least*, this horrific C-like workaround should be possible:

void foo()
{
    void b();
    void a() {...};
    void b() {...};
}

...Flame away! ;)


April 03, 2012
> Sorry, I didn't mean this to go into "D.announce". Reposting in the proper place... Can this one be deleted?

Off Topic: In Gmail, it applied both labels to the one email, which is cool :D.

Otherwise I think that the C-like workaround should be ok, the issue is with closures, what values should be visible to the closures?
--
James Miller
April 03, 2012
Le 03/04/2012 07:38, Nick Sabalausky a écrit :
> Regarding this:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=790
>
> I submit that nested functions should be exempt from the usual sequential
> visibility rules. (Therefore, mutually recursive nested functions would
> become possible.)
>
> Or at the very *least*, this horrific C-like workaround should be possible:
>
> void foo()
> {
>      void b();
>      void a() {...};
>      void b() {...};
> }
>
> ...Flame away! ;)
>

This is a +1 .
April 03, 2012
On 03/04/12 07:38, Nick Sabalausky wrote:
> Regarding this:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=790
>
> I submit that nested functions should be exempt from the usual sequential
> visibility rules. (Therefore, mutually recursive nested functions would
> become possible.)
>
> Or at the very *least*, this horrific C-like workaround should be possible:
>
> void foo()
> {
>      void b();
>      void a() {...};
>      void b() {...};
> }
>
> ...Flame away! ;)
>

This is asking for a complicated special case. In global scope, order of declarations doesn't matter. In function scope, order of declarations always matters.
If you have type inference of function returns, things can get nasty:

void foo()
{
     auto b() { return a(); }
     X x = whatever;
     auto a() { return x; }
}
Now b actually depends on the declaration of x. So it's not enough to say that only function declarations are immune to ordering rules.
Furthermore, any declaration before x, which calls b(), is using x even though x hasn't been initialized (or even declared) yet. Suddenly all kinds of horrible situations become possible, which could never happen before.

In general, allowing forward references inside a function is far, far more difficult than in global scope. There are very many more special cases. Allowing it in global scope is quite complicated enough.

You can always use a delegate, if you want recursive nested functions.
April 03, 2012
"James Miller" <james@aatch.net> wrote in message news:mailman.1324.1333434077.4860.digitalmars-d@puremagic.com...
>
> [...]the issue
> is with closures, what values should be visible to the closures?

Personally, I don't much care ATM. Either way has it's pros and cons:

A. Same as right now: The nested function can only access variables defined before it. Only difference is that it can *also* access "sibling" nested functions that are defined before *or* after. The benefit is that there's minimal change to the current "sequential visibility".

B. Let nested funcs access *any* "sibling" symbol whether defined before or after. Presumably, the nested func would not be *callable* until after all the sibling symbols it accesses have been declared. The downside is that, I suspect, this might be more work to implement. The benefits are that it provides more flexibility and is more consistent with module-level declarations. That latter benefit would be import for code like this:

void main() {  mixin(import("script.d"));  }

I guess I would lean more towards "B", but it is a bigger change and I realize bigger changes meet bigger resistance at this point. So I'd be content either way because what's *more* important here is we ditch the (seemingly) silly restriction of "no mutually recursive nested funcs".

'Course, another approach would be to just for the compiler to treat this:

void foo()
{
    /+ code 1 +/
    void a() {...};
    /+ code 2 +/
    void b() {...};
    /+ code 3 +/
}

Like this:

void foo()
{
    void delegate() a;
    void delegate() b;

    /+ code 1 +/
    a = () {...};
    /+ code 2 +/

    b = () {...};
    /+ code 3 +/

    // Except that, naturally, a and b cannot
    // be re-assigned by the programmer,
    // and foo itself cannot access a or b
    // before the "a = ...;" and "b = ...;" lines
    // respectively.
}

This maybe makes the most sense, because nested funcs and anon-funcs-assigned-to-a-variable *are* so very similar anyway. But then again, this comes with the downside of "void main() { mixin(import("script.d"));  }" causing strange semantics inside 'script.d'...but I guess such strange semantics in 'script.d' would be the case even without any nested funcs involved.

Actually, this might be equivalent to approach "A" above.


April 03, 2012
"Don Clugston" <dac@nospam.com> wrote in message news:jlecab$9gh$1@digitalmars.com...
>
> If you have type inference of function returns, things can get nasty:
>
> void foo()
> {
>      auto b() { return a(); }
>      X x = whatever;
>      auto a() { return x; }
> }
> Now b actually depends on the declaration of x. So it's not enough to say
> that only function declarations are immune to ordering rules.

Does being inside a function really make the type deduction any harder (or different?) than this?:

auto b() { return a(); }
enum X x = whatever;
auto a() { return x; }

> Furthermore, any declaration before x, which calls b(), is using x even though x hasn't been initialized (or even declared) yet. Suddenly all kinds of horrible situations become possible, which could never happen before.
>

(Don't know if this makes any sence, but I'm throwing it out there...)

Suppose we did the approach I mentioned elsewhere in this thread: "the compiler rewrites nested func decls as delegate vars and anon funcs". Suppose we also use the rule:

"A nested func is not *callable* (by either the parent function *or* another nested function) until after (ie "further down in the code") all the sibling symbols it accesses have been declared."

If it's reasonable for type deduction to happen before all this (I have no idea how realistic that is), then with your example, the compiler first deduces the types just like it would outside a function:

void foo()
{
    X b() { return a(); }
    X x = whatever;
    X a() { return x; }
}

Then it gets rewritten:

void foo()
{
    delegate X() b;
    delegate X() a;

    b = X() { return a(); }
    X x = whatever;
    a = X() { return x; }
}

This would now be flagged as an error because "b" is calling "a" before (ie "earlier in the code than") all of the symbols "a" accesses (namely "x") have been declared. The following would also be an error for the same reason:

void foo()
{
    auto b() { return a(); }
    X w = b();
    X x = whatever;
    auto a() { return x; }
}

However, this would still be perfectly ok:

void foo()
{
    X x = whatever;
    auto b() { return a(); }
    auto a() { return x; }
}

Because it turns into:

void foo()
{
    delegate X() b;
    delegate X() a;

    X x = whatever;
    b = X() { return a(); }
    a = X() { return x; }
}

And now, at the point in the code where "b" calls "a", everything "a" accesses (namely "x") has *already* been declared and inited. And of couse, "b" *can* call "a" because "a" is already declared way at the top.


April 03, 2012
On 03/04/12 11:24, Nick Sabalausky wrote:
> "Don Clugston"<dac@nospam.com>  wrote in message
> news:jlecab$9gh$1@digitalmars.com...
>>
>> If you have type inference of function returns, things can get nasty:
>>
>> void foo()
>> {
>>       auto b() { return a(); }
>>       X x = whatever;
>>       auto a() { return x; }
>> }
>> Now b actually depends on the declaration of x. So it's not enough to say
>> that only function declarations are immune to ordering rules.
>
> Does being inside a function really make the type deduction any harder (or
> different?) than this?:
>
> auto b() { return a(); }
> enum X x = whatever;
> auto a() { return x; }

Yes, it's different. In the second case, X is entirely determined at compile time. If you have multiple declarations, there are no user-visible semantics which are order-dependent; the value of the expression is independent of its location in the file.

This is not true of declarations inside a scope:

eg,

   static int count = 0;
   int order() { ++count; return count; }

   int x = order();
   int y = order();

   assert(x == 1);
   assert(y == 2);

>> Furthermore, any declaration before x, which calls b(), is using x even
>> though x hasn't been initialized (or even declared) yet. Suddenly all
>> kinds of horrible situations become possible, which could never happen
>> before.
>>
>
> (Don't know if this makes any sence, but I'm throwing it out there...)
>
> Suppose we did the approach I mentioned elsewhere in this thread: "the
> compiler rewrites nested func decls as delegate vars and anon funcs".
> Suppose we also use the rule:
>
> "A nested func is not *callable* (by either the parent function *or* another
> nested function) until after (ie "further down in the code") all the sibling
> symbols it accesses have been declared."

Difficult but doable, but I think you end up with a really funky rule.
It's a morass of special cases.
April 03, 2012
On 04/03/2012 07:38 AM, Nick Sabalausky wrote:
> Regarding this:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=790
>
> I submit that nested functions should be exempt from the usual sequential
> visibility rules. (Therefore, mutually recursive nested functions would
> become possible.)
>
> Or at the very *least*, this horrific C-like workaround should be possible:
>
> void foo()
> {
>      void b();
>      void a() {...};
>      void b() {...};
> }
>
> ...Flame away! ;)
>
>

This is the right way to work around this issue. It works now and does not imply any kind of overhead at runtime:

void foo(){
    void a()(){ ... }
    void b()  { ... }
}

However, I agree. Local functions that appear directly in sequence should be able to forward-reference each other. If some functions appearing in such a sequence have identical names, they should overload against each other.
April 03, 2012
On 04/03/2012 10:27 AM, Don Clugston wrote:
> On 03/04/12 07:38, Nick Sabalausky wrote:
>> Regarding this:
>>
>> http://d.puremagic.com/issues/show_bug.cgi?id=790
>>
>> I submit that nested functions should be exempt from the usual sequential
>> visibility rules. (Therefore, mutually recursive nested functions would
>> become possible.)
>>
>> Or at the very *least*, this horrific C-like workaround should be
>> possible:
>>
>> void foo()
>> {
>>      void b();
>>      void a() {...};
>>      void b() {...};
>> }
>>
>> ...Flame away! ;)
>>
>
> This is asking for a complicated special case. In global scope, order of
> declarations doesn't matter. In function scope, order of declarations
> always matters.
> If you have type inference of function returns, things can get nasty:
>
> void foo()
> {
>       auto b() { return a(); }
>       X x = whatever;
>       auto a() { return x; }
> }
> Now b actually depends on the declaration of x. So it's not enough to
> say that only function declarations are immune to ordering rules.

I come to a different conclusion. If only function declarations are immune to ordering rules, the above example is simply illegal. The example cannot be used to demonstrate incompleteness of the approach.

> Furthermore, any declaration before x, which calls b(), is using x even
> though x hasn't been initialized (or even declared) yet. Suddenly all
> kinds of horrible situations become possible, which could never happen
> before.
>
> In general, allowing forward references inside a function is far, far
> more difficult than in global scope.

If it is constrained enough, it shouldn't be more difficult.

> There are very many more special
> cases. Allowing it in global scope is quite complicated enough.
>

It should be possible to leverage the efforts spent there.

> You can always use a delegate, if you want recursive nested functions.

Templates are superior.

April 03, 2012
On 03/04/12 12:32, Timon Gehr wrote:
> On 04/03/2012 10:27 AM, Don Clugston wrote:
>> On 03/04/12 07:38, Nick Sabalausky wrote:
>>> Regarding this:
>>>
>>> http://d.puremagic.com/issues/show_bug.cgi?id=790
>>>
>>> I submit that nested functions should be exempt from the usual
>>> sequential
>>> visibility rules. (Therefore, mutually recursive nested functions would
>>> become possible.)
>>>
>>> Or at the very *least*, this horrific C-like workaround should be
>>> possible:
>>>
>>> void foo()
>>> {
>>> void b();
>>> void a() {...};
>>> void b() {...};
>>> }
>>>
>>> ...Flame away! ;)
>>>
>>
>> This is asking for a complicated special case. In global scope, order of
>> declarations doesn't matter. In function scope, order of declarations
>> always matters.
>> If you have type inference of function returns, things can get nasty:
>>
>> void foo()
>> {
>> auto b() { return a(); }
>> X x = whatever;
>> auto a() { return x; }
>> }
>> Now b actually depends on the declaration of x. So it's not enough to
>> say that only function declarations are immune to ordering rules.
>
> I come to a different conclusion. If only function declarations are
> immune to ordering rules, the above example is simply illegal. The
> example cannot be used to demonstrate incompleteness of the approach.

I don't see a way to just declare it as "illegal". How would you detect that situation in the general case?
It's not easy.

Y b() { ... }
Y y = b();
X x = ...

Prove that y doesn't depend on x.
« First   ‹ Prev
1 2 3