[M3devel] [modula3/cm3] Quake scoping issue (#19)

JC Chu jcchu at acm.org
Mon Aug 28 16:59:38 CEST 2017


> The textual inclusion is not really relevant.  It's entirely the bizarre semantics of a procedure definition occurring inside an inner scope such as another procedure of a for statement.  It doesn't matter whether/how inclusion was involved in getting the "nested" procedure into its nested position.

Yeah.  I was talking about why `list` was overriden and became an array in the first example (because of “include”) while `test_var` was not overriden in the second example (because of how scoping works); no procedure definitions was involved so far.

> It would seem so much more sensible to just disallow a procedure definition altogether in a nested position.  That would have meant included Quake code that declared its own procedure(s) could only be included from directly inside the global scope.  Would that be such a burdensome restriction?

Upon a second look, the semantics of nested procedure declarations doesn’t seem entirely bizarre to me.  The current rule seems to be that any identifier not declared locally is searched _only_ in the global scope, as the following example shows (based on your previous example).

	test_var = 0
	
	proc P (test_var) is
	  write(format("P: (%s, %s)\n", test_var, test_var_2))
	  proc Q () is write(format("Q: (%s, %s)\n", test_var, test_var_2)) end
	  return Q
	end
	
	proc test () is
	  test_var_2 = 456
          local test_var_2 = 654  % no effect
	  local q = P(123)
	  q()
	end
	
	test()

In other words, at any point, there are only two possible scopes: local (to the current procedure only, not nested ones) and global.  This rule implies that all procedures will behave as if they were declared in the global scope.  It also seems to be the reason why procedures “may be defined in the global scope only” (at least by the book).

With the identifier searching rule in mind, I don’t think procedures in nested positions necessarily cause any confusion or should be forbidden.

Instead, the real culprit is the macro-like behavior of `include` and `include_dir` (and arguably `quake`), which can cause global identifiers to be overridden when the inclusion is non-global.

— JC

-----Original Message-----
From: Rodney M. Bates [mailto:rodney_bates at lcwb.coop] 
Sent: Monday, August 28, 2017 0:52
To: JC Chu <jc at latemarch.com>; m3devel at elegosoft.com; JC Chu <jcchu at acm.org>
Subject: Re: [M3devel] [modula3/cm3] Quake scoping issue (#19)



On 08/26/2017 08:18 PM, JC Chu wrote:
>> Note that this is static scoping, not dynamic, which means the containing scope of 'write_test_var' is not the scope of its caller, but is determined by its textual location within the code, specifically, it is immediately inside the global scope.
>
> Hmm, let’s see if I understand this whole thing correctly.  The procedure `include`, according to Quake doc §9.2, performs in-place expansion, like a macro.  When a makefile is expanded from within a procedure, as in my first example, it’s interpreted against that procedure’s local scope, where global identifiers may be overridden.  This may give the false impression that Quake is not statically scoped, which is then refuted by my second example.
>

The textual inclusion is not really relevant.  It's entirely the bizarre semantics of a
procedure definition occurring inside an inner scope such as another procedure of a for
statement.  It doesn't matter whether/how inclusion was involved in getting the "nested"
procedure into its nested position.

It would seem so much more sensible to just disallow a procedure definition altogether
in a nested position.  That would have meant included Quake code that declared its own
procedure(s) could only be included from directly inside the global scope.  Would that
be such a burdensome restriction?

Actually, maybe the designers intended it to be that way, but the implementation just
has a bug not enforcing the rule.  In fact, the language definition said (before my
update to it just now) "Procedures may be defined in the global scope only.", which
really ought to be interpreted to mean just what I am suggesting.

I wonder how much existing Quake code would break if we did that?


> — JC
>
> -----Original Message-----
> From: Rodney M. Bates [mailto:rodney_bates at lcwb.coop]
> Sent: Sunday, August 27, 2017 0:02
> To: m3devel at elegosoft.com; JC Chu <jcchu at acm.org>
> Subject: Re: [M3devel] [modula3/cm3] Quake scoping issue (#19)
>
>
>
> On 08/25/2017 07:55 PM, JC Chu wrote:
>> I understand from §7.2 and your comment that, when “m3makefile2” is included, it is interpreted inside the local scope created when `map_proc` is called, where `list` is not bound to a procedure.  In fact, even if “m3makefile2” didn’t define `list` itself, the same error would still occur.  (That said, if procedures can only be defined in the global scope, shouldn’t they only be searched there as well?)
>>
>
> Well, I am not trying to justify or defend this language design, just understand what it is.
> This is indeed unusual.  I don't recall ever seeing a language where the scope an indentifier
> is declared in is different from the scope the declaring code is located in.  In fact, I find
> it very difficult to articulate this clearly, and I don't think the foregoing sentence does it
> very well.
>
>> However, by the same argument, the behavior of the following makefile is a bit unexpected to me.
>>
>>     test_var = 0
>>     readonly proc call (f, test_var) is return f() end
>>     readonly proc write_test_var () is
>>       write(format("test_var = %s\n"), test_var)
>>     end
>>     readonly proc eval (_) is end
>>     eval(call(write_test_var, 123))
>>
>> Here, `write_test_var` is evaluated in its own scope, which is inside the scope of `call`, where `test_var` = 123.  The actual output, though, is 0.
>>
>
> This makes complete sense to me, after a rather intense reading session.  'call' is called
> with 'write_test_var' and 123 as actual parameters corresponding to 'call's formals 'f'
> and 'test_var'.  But 'call's body makes no reference to its formal 'test_var', so the 123 value
> is just lost.  When execution gets into 'write_test_var', its containing scope is the global
> scope, where the global 'test_var' is the one found.
>
> Note that this is static scoping, not dynamic, which means the containing scope of 'write_test_var'
> is not the scope of its caller, but is determined by its textual location within the code,
> specifically, it is immediately inside the global scope.
>
> Dynamic scoping, e.g., in lisp, has turned out to be a source of intriguing puzzles, but far
> too confusing to be helpful to programming in general.  It makes it too easy for something to
> change meaning unexpectedly, as result of being called from some place unknown to the writer
> of the procedure.
>
>> — JC
>>
>> -----Original Message-----
>> From: Rodney M. Bates [mailto:rbatesjk at lcwb.coop]
>> Sent: Friday, August 25, 2017 23:40
>> To: reply+00655cb4faee270b469769c158f8bcd114e2942747144a0292cf0000000115b6154992a169ce0f0c9468 at reply.github.com; m3devel <m3devel at elegosoft.com>; jcchu >> JC Chu <jcchu at acm.org>
>> Subject: Re: [modula3/cm3] Quake scoping issue (#19)
>>
>> According to Quake, 7.3, "Procedures may be defined in the global scope only".
>> Apparently, from experiment, this means that, although the procedure definition
>> itself can be located inside a local scope, its name is declared in the global
>> scope.
>>
>> So, if you don't import libm3, your 'list' declaration is not a duplicate, thus
>> produces no error, and becomes declared in the global scope.  But the call on
>> 'list' resolves to the formal parameter 'list', of 'map_proc', which is found
>> in the local scope, and whose value is the list '[ m3makefile2 ]', not a
>> procedure, and thus not callable.
>>
>> Any suitable renaming of either the parameter or the procedure, so that the two
>> meanings of 'list' have different names, works as expected.
>>
>> I'll grant that having a procedure declaration located in a local scope but
>> declaring its name in the global one is peculiar, and this is a peculiar
>> effect of that rule.
>>
>> I have made a note to explain this better in the Quake documentation.
>>
>> On 08/23/2017 11:17 PM, jcchu wrote:
>>> See demo.zip <https://github.com/modula3/cm3/files/1247702/demo.zip>.
>>>
>>>>>> You are receiving this because you are subscribed to this thread.
>>> Reply to this email directly, view it on GitHub <https://github.com/modula3/cm3/issues/19>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AGVctCYptEcKU12OG82H67xEwMAkKPkNks5sbPlJgaJpZM4PA3zO>.
>>>
>> _______________________________________________
>> M3devel mailing list
>> M3devel at elegosoft.com
>> https://m3lists.elegosoft.com/mailman/listinfo/m3devel
>>
>

-- 
Rodney Bates
rodney.m.bates at acm.org


More information about the M3devel mailing list