Skip to content

Use procedure scope for modules #560

@capocasa

Description

@capocasa

Abstract

Module scope should be discontinued and procedure scope used in all modules, including the main module.

Global variables continue to be available for use using the .global. pragma.

Various non-variable related statements can still be module only, such as types and imports.

Motivation

Most code is written in procedures. Yet, when one writes a small program in Nim, the first code one writes is in module scope.

It is a not particularly safe to hand implicitly declared global variables to a new Nim user, and it is a source of friction for experienced Nim user. Will this throwaway program still work if I wrap it into a procedure? Will these procs I declared have closure or nimcall signature, and which does the library expect when I pass them as parameters? If I remove the indent of a var statement at the end of a proc, will I notice I have created a global variable?

If the main module and all modules would use procedure scope by default, there would be no such issues. Global variables (and thread vars) could still be declared with the global pragma when they are needed, and they would be immediately obvious.

Finally, the Nim main module is a procedure in terms of implementation.

TLDR; Create simplicity and elegance, remove friction and improve safety and readability by not having a separate scope for modules.

Description

Implementation details are unclear to me but I suspect they work using global program memory instead of stack memory for modules. I suggest creating a first stack entry early so module memory management will work like that of procedures.

The downside is that global-memory-by-default is no longer available in Nim. Since in the C implementation, modules are functions anyway, I don't suspect that this would be missed, especially since a workaround is available in the form of the {.global.} pragma.

It is of course a breaking change and that should not be taking lightly. Applying patches should be straight forward and is possibly semi-automatable.

Code Examples

Current situation

# this is a global, but only indentation makes that clear
# it could be a local var if this file was included even if not indented
var foo = 1

proc addHandler(proc() {.closure.}) =
  discard

proc bar() =
  # this is nimcall
  echo foo

addHandler(bar)  # this fails

proc fuz() =
  var foo = 1
  proc foo() =
    # this is a closure
    echo foo
  addHandler(foo)  # this works

With the change

# this is always a local variable without any pragmas
var foo = 1

proc addHandler(proc() {.closure.}) =
  discard

proc bar() =
  # this is a closure
  echo foo

addHandler(bar)  # this works

proc fuz() =
  var foo = 1
  proc foo() =
    # this is a closure
    echo foo
  addHandler(foo)  # this works

# this is a global, it is clear at first glance because of the pragma
var fuz {.global.} = 1

proc buz() =
  # this is nimcall, but it is expected, because the pragma had to be added explicitly
  echo buz

Backwards Compatibility

The entire proposal is of course a breaking change and that should not be taking lightly. Applying patches should be straight forward and is possibly semi-automatable. It also doesn't affect most code at all, because most code is written in procedures.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions