Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise module system #2

Open
gvx opened this issue Mar 23, 2014 · 0 comments
Open

Revise module system #2

gvx opened this issue Mar 23, 2014 · 0 comments

Comments

@gvx
Copy link
Owner

gvx commented Mar 23, 2014

Currently, a module works by returning a value that will be that module's "module value":

local f:
    42

{ :f @f }

This is for the most part an acceptable way of doing things. One problem is that it hinders circular imports from being handled gracefully, since the exported object isn't available until after the importing has finished.

There are several possible solutions to this. One of my favourite involves creating an extra scope for modules, that counts as a global scope when writing. That is, modules are no longer allowed to mess with the global scope. If a module attempts to set a global variable, it is exported instead. (An extra scope is inserted so that we still can use module locals.)

# m.deja
f:
    42

# some other file
!import!m

!. m!f # prints 42

This way, we can change the compiler code like this:

diff --git a/vm/compiler.deja b/vm/compiler.deja
index e10a24c..0c7cbd3 100644
--- a/vm/compiler.deja
+++ b/vm/compiler.deja
@@ -802,18 +802,19 @@ local make-bytecode opcodes literals:
        !compile-string src
        !write dst

-!use mname:
+!use mname env:
        !find-module mname
        if dup:
                !compile swap over
        else:
                drop
-       !run-file-in { :(name) mname }
+       !run-file-in env

 local :loaded {}
 !import m:
        if not has loaded m:
-               set-to loaded m !use m
+               set-to loaded m dup { :(name) m }
+               !use m
        m @loaded! m
        tail-call @local

This changes the signature of !use but oh well.

Alternatives considered:

  • Before running a module, set its corresponding value in loaded to some special sentinel object. When entering !import, check for that sentinel. If an import cycle was found, raise an error. By far the simplest solution to implement, but it would prevent some valid ways to organize modules from being usable.
  • Using a module local dictionary called export. This is a bit more finicky and verbose.
  • Make !import do a tail-call on set instead of on local. That way, modules imported in sub-modules are automatically exported. However, this results in inconvenient syntax and would likely only violate the Law of Demeter anyway.
  • Use a proxy object as sentinel. This means code can keep looking like the first example, while supporting the functionality of the main proposal. A disadvantage would be the need to implement proxy objects for a very specific and narrow purpose. Also, backwards compatibility is not a concern for Déjà Vu right now.

Disadvantages of implementing this LIT:

  • Parts of the VM need to be rewritten to accommodate for the extra scope inserted, as well as providing a mechanism for locking the global scope.
  • Module values can no longer be anything other than dictionaries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant