mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-14 23:58:03 +01:00
331 lines
17 KiB
HTML
331 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||
<title>Modules – Wren</title>
|
||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||
the viewport. -->
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||
</head>
|
||
<body id="top">
|
||
<header>
|
||
<div class="page">
|
||
<div class="main-column">
|
||
<h1><a href="./">wren</a></h1>
|
||
<h2>a classy little scripting language</h2>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div class="page">
|
||
<nav>
|
||
<ul>
|
||
<li><a href="getting-started.html">Getting Started</a></li>
|
||
</ul>
|
||
<section>
|
||
<h2>language</h2>
|
||
<ul>
|
||
<li><a href="syntax.html">Syntax</a></li>
|
||
<li><a href="expressions.html">Expressions</a></li>
|
||
<li><a href="variables.html">Variables</a></li>
|
||
<li><a href="control-flow.html">Control Flow</a></li>
|
||
<li><a href="error-handling.html">Error Handling</a></li>
|
||
<li><a href="modules.html">Modules</a></li>
|
||
</ul>
|
||
</section>
|
||
<section>
|
||
<h2>types</h2>
|
||
<ul>
|
||
<li><a href="values.html">Values</a></li>
|
||
<li><a href="classes.html">Classes</a></li>
|
||
<li><a href="fibers.html">Fibers</a></li>
|
||
<li><a href="functions.html">Functions</a></li>
|
||
<li><a href="lists.html">Lists</a></li>
|
||
<li><a href="maps.html">Maps</a></li>
|
||
</ul>
|
||
</section>
|
||
<section>
|
||
<h2>reference</h2>
|
||
<ul>
|
||
<li><a href="core">Core Library</a></li>
|
||
<li><a href="embedding-api.html">Embedding API</a></li>
|
||
<li><a href="performance.html">Performance</a></li>
|
||
<li><a href="community.html">Community</a></li>
|
||
<li><a href="contributing.html">Contributing</a></li>
|
||
<li><a href="qa.html">Q & A</a></li>
|
||
</ul>
|
||
</section>
|
||
</nav>
|
||
<main>
|
||
<h1>Modules</h1>
|
||
<p>Once you start writing programs that are more than little toys, you quickly run
|
||
into two problems:</p>
|
||
<ol>
|
||
<li>
|
||
<p>You want to break them down into multiple smaller files to make it easier to
|
||
find your way around them.</p>
|
||
</li>
|
||
<li>
|
||
<p>You want to reuse pieces of them across different programs.</p>
|
||
</li>
|
||
</ol>
|
||
<p>To address those, Wren has a simple module system. A file containing Wren code
|
||
defines a <em>module</em>. A module can use the code defined in another module by
|
||
<em>importing</em> it. You can break big programs into smaller modules that you
|
||
import, and you can reuse code by having multiple programs share the use of a
|
||
single module.</p>
|
||
<p>Wren does not have a single global scope. Instead, each module has its own
|
||
top-level scope independent of all other modules. This means, for example, that
|
||
two modules can define a top-level variable with the same name without causing
|
||
a name collision. Each module is, well, modular.</p>
|
||
<h2>Importing, briefly <a href="#importing,-briefly" name="importing,-briefly" class="header-anchor">#</a></h2>
|
||
<p>When you run Wren and give it a file name to execute, the contents of that file
|
||
define the "main" module that execution starts at. To load and execute other
|
||
modules, you use an import statement:</p>
|
||
<div class="codehilite"><pre><span class="k">import</span> <span class="s">"beverages"</span> <span class="k">for</span> <span class="vg">Coffee</span><span class="p">,</span> <span class="vg">Tea</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>This finds a module named "beverages" and executes its source code. Then, it
|
||
looks up two top-level variables, <code>Coffee</code> and <code>Tea</code> in <em>that</em> module and
|
||
creates new variables in <em>this</em> module with their values.</p>
|
||
<p>This statement can appear anywhere a variable declaration is allowed, even
|
||
inside blocks:</p>
|
||
<div class="codehilite"><pre><span class="k">if</span> <span class="p">(</span><span class="n">thirsty</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="k">import</span> <span class="s">"beverages"</span> <span class="k">for</span> <span class="vg">Coffee</span><span class="p">,</span> <span class="vg">Tea</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>If you want to load a module, but not bind any variables from it, you can omit
|
||
the <code>for</code> clause:</p>
|
||
<div class="codehilite"><pre><span class="k">import</span> <span class="s">"some_imperative_code"</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>That's the basic idea. Now let's break it down into each of the steps it
|
||
performs:</p>
|
||
<ol>
|
||
<li>Locate the source code for the module.</li>
|
||
<li>Execute the imported module's code.</li>
|
||
<li>Bind new variables in the importing module to values defined in the imported module.</li>
|
||
</ol>
|
||
<p>We'll go through each step:</p>
|
||
<h2>Locating a module <a href="#locating-a-module" name="locating-a-module" class="header-anchor">#</a></h2>
|
||
<p>The first thing you need to do to import a module is actually <em>find</em> the code
|
||
for it. The import specifies a <em>name</em>—some arbitrary string that is used
|
||
to uniquely identify the module. The embedding application controls how that
|
||
string is used to locate a blob of source code.</p>
|
||
<p>When the host application creates a new Wren VM, it provides a module loader
|
||
function:</p>
|
||
<div class="codehilite"><pre><span class="n">WrenConfiguration</span> <span class="n">config</span><span class="p">;</span>
|
||
<span class="n">config</span><span class="p">.</span><span class="n">loadModuleFn</span> <span class="o">=</span> <span class="n">loadModule</span><span class="p">;</span>
|
||
|
||
<span class="c1">// Other configuration...</span>
|
||
|
||
<span class="n">WrenVM</span><span class="o">*</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">wrenNewVM</span><span class="p">(</span><span class="o">&</span><span class="n">config</span><span class="p">);</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>That function has this signature:</p>
|
||
<div class="codehilite"><pre><span class="kt">char</span><span class="o">*</span> <span class="nf">WrenLoadModuleFn</span><span class="p">(</span><span class="n">WrenVM</span><span class="o">*</span> <span class="n">vm</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">name</span><span class="p">);</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Whenever a module is imported, the VM calls this and passes it the name of the
|
||
module. The embedder is expected to return the source code contents of the
|
||
module. When you embed Wren in your app, you can handle this however you want:
|
||
reach out to the file system, look inside resources bundled into your app,
|
||
whatever.</p>
|
||
<p>You can return <code>NULL</code> from this function to indicate that a module couldn't be
|
||
found. When you do this, Wren will report it as a runtime error.</p>
|
||
<h3>The command-line loader <a href="#the-command-line-loader" name="the-command-line-loader" class="header-anchor">#</a></h3>
|
||
<p>The default little command-line VM that comes with Wren has a very simple
|
||
lookup process. It appends the module name and ".wren" to the directory where
|
||
the main module was loaded and looks for that file. So, let's say you run:</p>
|
||
<div class="codehilite"><pre><span class="nv">$ </span>wren /code/my_program.wren
|
||
</pre></div>
|
||
|
||
|
||
<p>And that main module has:</p>
|
||
<div class="codehilite"><pre><span class="k">import</span> <span class="s">"some/module"</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Then the command-line VM will try to find <code>/code/some/module.wren</code>. By
|
||
convention, forward slashes should be used as path separators, even on Windows,
|
||
to help ensure your scripts are platform-independent. (Forward slashes are a
|
||
valid separator on Windows, but backslashes are not valid on other OSes.)</p>
|
||
<h2>Executing the module <a href="#executing-the-module" name="executing-the-module" class="header-anchor">#</a></h2>
|
||
<p>Once we have the source code for a module, we need to run it. First, the VM
|
||
takes the fiber that is executing the <code>import</code> statement in the importing
|
||
module and pauses it.</p>
|
||
<p>Then it creates a new module object—a new fresh top-level scope,
|
||
basically—and a new fiber. It executes the new module's code in that
|
||
fiber and scope. The module can run whatever imperative code it wants and
|
||
define whatever top-level variables it wants.</p>
|
||
<p>When the module's code is done being executed and its fiber completes, the
|
||
suspended fiber for the importing module is resumed. This suspending and
|
||
resuming is recursive. So, if "a" imports "b" which imports "c", both "a" and
|
||
"b" will be suspended while "c" is running. When "c" is done, "b" is resumed.
|
||
Then, when "b" completes, "a" is resumed.</p>
|
||
<p>Think of it like traversing the tree of imports, one node at a time. At any
|
||
given point in time, only one module's code is running.</p>
|
||
<h2>Binding variables <a href="#binding-variables" name="binding-variables" class="header-anchor">#</a></h2>
|
||
<p>Once the module is done executing, the last step is to actually <em>import</em> some
|
||
data from it. Any module can define "top-level" <a href="variables.html">variables</a>.
|
||
These are simply variables declared outside of any
|
||
<a href="classes.html#methods">method</a> or <a href="functions.html">function</a>.</p>
|
||
<p>These are visible to anything inside the module, but they can also be
|
||
<em>exported</em> and used by other modules. When Wren executes an import like:</p>
|
||
<div class="codehilite"><pre><span class="k">import</span> <span class="s">"beverages"</span> <span class="k">for</span> <span class="vg">Coffee</span><span class="p">,</span> <span class="vg">Tea</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>First it runs the "beverages" module. Then it goes through each of the variable
|
||
names in the <code>for</code> clause. For each one, it looks for a top-level variable with
|
||
that name in the imported module. If a variable with that name can't be found
|
||
in the imported module, it's a runtime error.</p>
|
||
<p>Otherwise, it gets the current value of the variable and defines a new variable
|
||
in the importing module with the same name and value. It's worth noting that
|
||
the importing module gets its <em>own</em> variable whose value is a snapshot of the
|
||
value of the imported variable at the time it was imported. If either module
|
||
later assigns to that variable, the other won't see it. It's not a "live"
|
||
connection.</p>
|
||
<p>In practice, most top-level variables are only assigned once anyway, so this
|
||
rarely makes a difference.</p>
|
||
<h2>Shared imports <a href="#shared-imports" name="shared-imports" class="header-anchor">#</a></h2>
|
||
<p>Earlier, I described a programs set of modules as a tree. Of course, it's only
|
||
a <em>tree</em> of modules if there are no <em>shared imports</em>. But consider a program
|
||
like:</p>
|
||
<div class="codehilite"><pre><span class="c1">// main.wren</span>
|
||
<span class="k">import</span> <span class="s">"a"</span>
|
||
<span class="k">import</span> <span class="s">"b"</span>
|
||
|
||
<span class="c1">// a.wren</span>
|
||
<span class="k">import</span> <span class="s">"shared"</span>
|
||
|
||
<span class="c1">// b.wren</span>
|
||
<span class="k">import</span> <span class="s">"shared"</span>
|
||
|
||
<span class="c1">// shared.wren</span>
|
||
<span class="vg">System</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s">"Shared!"</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Here, "a" and "b" both want to use "shared". If "shared" defines some top-level
|
||
state, we only want a single copy of that in memory. To handle this, a module's
|
||
code is only executed the <em>first</em> time it is loaded. After that, importing the
|
||
module again just looks up the previously loaded module.</p>
|
||
<p>Internally, Wren maintains a map of every module it has previously loaded. When
|
||
a module is imported, Wren looks for it in that map first before it calls out
|
||
to the embedder for its source.</p>
|
||
<p>In other words, in that list of steps above, there's an implicit zeroth step:
|
||
"See if we already loaded the module and reuse it if we did". That means the
|
||
above program only prints "Shared!" once.</p>
|
||
<h2>Cyclic imports <a href="#cyclic-imports" name="cyclic-imports" class="header-anchor">#</a></h2>
|
||
<p>You can even have cycles in your imports, provided you're a bit careful with
|
||
them. The loading process, in detail, is:</p>
|
||
<ol>
|
||
<li>See if we have already created a module with the given name.</li>
|
||
<li>If so, use it.</li>
|
||
<li>Otherwise, create a new module with the name and store it in the module
|
||
registry.</li>
|
||
<li>Create a fiber for it and execute its code.</li>
|
||
</ol>
|
||
<p>Note the order of the last two steps. When a module is loaded, it is added to
|
||
the registry <em>before</em> it is executed. This means if an import for that same
|
||
module is reached while the module itself or one of its imports is executing,
|
||
it will be found in the registry and the cycle is short-circuited.</p>
|
||
<p>For example:</p>
|
||
<div class="codehilite"><pre><span class="c1">// main.wren</span>
|
||
<span class="k">import</span> <span class="s">"a"</span>
|
||
|
||
<span class="c1">// a.wren</span>
|
||
<span class="vg">System</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s">"start a"</span><span class="p">)</span>
|
||
<span class="k">import</span> <span class="s">"b"</span>
|
||
<span class="vg">System</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s">"end a"</span><span class="p">)</span>
|
||
|
||
<span class="c1">// b.wren</span>
|
||
<span class="vg">System</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s">"start b"</span><span class="p">)</span>
|
||
<span class="k">import</span> <span class="s">"a"</span>
|
||
<span class="vg">System</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="s">"end b"</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>This program runs successfully and prints:</p>
|
||
<div class="codehilite"><pre>start a
|
||
start b
|
||
end b
|
||
end a
|
||
</pre></div>
|
||
|
||
|
||
<p>Where you have to be careful is binding variables. Consider:</p>
|
||
<div class="codehilite"><pre><span class="c1">// main.wren</span>
|
||
<span class="k">import</span> <span class="s">"a"</span>
|
||
|
||
<span class="c1">// a.wren</span>
|
||
<span class="k">import</span> <span class="s">"b"</span> <span class="k">for</span> <span class="err">B</span>
|
||
<span class="k">var</span> <span class="err">A</span> <span class="o">=</span> <span class="s">"a variable"</span>
|
||
|
||
<span class="c1">// b.wren</span>
|
||
<span class="k">import</span> <span class="s">"a"</span> <span class="k">for</span> <span class="err">A</span>
|
||
<span class="k">var</span> <span class="err">B</span> <span class="o">=</span> <span class="s">"b variable"</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>The import of "a" in b.wren will fail here. If you trace the execution, you
|
||
get:</p>
|
||
<ol>
|
||
<li>Execute <code>import "a"</code> in "main.wren". That suspends "main.wren".</li>
|
||
<li>Execute <code>import "b"</code> in "a.wren". That suspends "a.wren".</li>
|
||
<li>Execute <code>import "a"</code> in "b.wren". Since "a" is already in the module map,
|
||
this does <em>not</em> suspend it.</li>
|
||
</ol>
|
||
<p>Instead, we look for a variable named <code>A</code> in that module. But it hasn't been
|
||
defined yet since "a.wren" is still sitting on the <code>import "b" for B</code> line
|
||
before the declaration. To get this to work, you would need to move the
|
||
variable declaration above the import:</p>
|
||
<div class="codehilite"><pre><span class="c1">// main.wren</span>
|
||
<span class="k">import</span> <span class="s">"a"</span>
|
||
|
||
<span class="c1">// a.wren</span>
|
||
<span class="k">var</span> <span class="err">A</span> <span class="o">=</span> <span class="s">"a variable"</span>
|
||
<span class="k">import</span> <span class="s">"b"</span> <span class="k">for</span> <span class="err">B</span>
|
||
|
||
<span class="c1">// b.wren</span>
|
||
<span class="k">import</span> <span class="s">"a"</span> <span class="k">for</span> <span class="err">A</span>
|
||
<span class="k">var</span> <span class="err">B</span> <span class="o">=</span> <span class="s">"b variable"</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Now when we run it, we get:</p>
|
||
<ol>
|
||
<li>Execute <code>import "a"</code> in "main.wren". That suspends "main.wren".</li>
|
||
<li>Define <code>A</code> in "a.wren".</li>
|
||
<li>Execute <code>import "b"</code> in "a.wren". That suspends "a.wren".</li>
|
||
<li>Execute <code>import "a"</code> in "b.wren". Since "a" is already in the module map,
|
||
this does <em>not</em> suspend it. It looks up <code>A</code>, which has already been defined,
|
||
and binds it.</li>
|
||
<li>Define <code>B</code> in "b.wren".</li>
|
||
<li>Complete "b.wren".</li>
|
||
<li>Look up <code>B</code> in "b.wren" and bind it in "a.wren".</li>
|
||
<li>Resume "a.wren".</li>
|
||
</ol>
|
||
<p>This sounds super hairy, but that's because cyclic dependencies are hairy in
|
||
general. The key point here is that Wren <em>can</em> handle them in the rare cases
|
||
where you need them.</p>
|
||
</main>
|
||
</div>
|
||
<footer>
|
||
<div class="page">
|
||
<div class="main-column">
|
||
<p>Wren lives <a href="https://github.com/munificent/wren">on GitHub</a> — Made with ❤ by <a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and <a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.</p>
|
||
<div class="main-column">
|
||
</div>
|
||
</footer>
|
||
</body>
|
||
</html>
|