Skip to content

Commit

Permalink
add docs for method replacement / world age
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash committed Sep 28, 2016
1 parent e63f540 commit 32709f4
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 1 deletion.
6 changes: 5 additions & 1 deletion doc/devdocs/ast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ These symbols appear in the ``head`` field of ``Expr``\s in lowered form.
Method
~~~~~~

A unique'd container describing the shared metadata for a single (unspecialized) method.
A unique'd container describing the shared metadata for a single method.

``name``, ``module``, ``file``, ``line``, ``sig`` - Metadata to uniquely identify the method
for the computer and the human
Expand All @@ -205,6 +205,8 @@ A unique'd container describing the shared metadata for a single (unspecialized)

``nargs``, ``isva``, ``called``, ``isstaged`` - Descriptive bit-fields for the source code of this Method.

``min-age`` / ``max-age`` - The range of world ages for which this method is visible.


MethodInstance
~~~~~~~~~~~~~~
Expand All @@ -231,6 +233,8 @@ See especially :ref:`devdocs-locks` for important details on how to modify these
may be put here (if ``jlcall_api == 2``), or it could be set to `nothing`
to just indicate ``rettype`` is inferred

``min-age`` / ``max-age`` - The range of world ages for which this method instance is valid.

``ftpr`` - The generic jlcall entry point

``jlcall_api`` - The ABI to use when calling ``fptr``. Some significant ones include:
Expand Down
113 changes: 113 additions & 0 deletions doc/manual/methods.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,119 @@ Julia its ability to abstractly express high-level algorithms decoupled
from implementation details, yet generate efficient, specialized code to
handle each case at run time.

Redefining Methods
------------------

When redefining a method or adding new methods,
it is important to realize that these changes don't take effect immediately.
This is key to Julia's ability to statically infer and compile code to run fast,
without the usual JIT tricks and overhead.
Indeed, any new method definition won't be visible to the current runtime environment,
including Tasks and Threads, or any previously defined ``@generated`` functions.
Let's start with an example to see what this means::

julia> function tryeval()
@eval newfun() = 1
newfun()
end
tryeval (generic function with 1 method)

julia> tryeval()
ERROR: MethodError: no method matching newfun()
The applicable method may be too new: running in world age xxxx1, while current world is xxxx2.
Closest candidates are:
newfun() at none:1 (method too new to be called from this world context.)
in tryeval() at none:1
...

julia> newfun()
1

In this example, observe that the new definition for ``newfun`` has been created,
but can't be immediately called. Future calls to ``newfun`` from the REPL work as expected.
But future calls to ``tryeval`` will continue to see the definition of ``newfun`` as it was
*at the previous statement at the REPL*. You may want to try this for yourself to see how it works.

The world age counter is a monotonically increasing value that tracks each delayed operation.
This allows describing "the set of method definitions visible to a runtime environment"
as simply a number.
It also allows comparing the methods available in two worlds just by comparing their ordinal value.
In the example above, we see that the "current world" (in which the method ``newfun()`` exists),
is one greater than the runtime world that was fixed when the execution of ``tryeval`` started.

Sometimes it is necessary to get around this (for example, if you are implementing the above REPL).
Well, don't despair, since there's an easy solution: just call ``eval`` a second time.
For example, here we create a zero-argument closure over ``ans`` and ``eval`` a call to it:

.. doctest::

julia> function tryeval2()
ans = (@eval newfun2() = 1)
res = eval(Expr(:call,
function()
return ans() + 1
end))
return res
end
tryeval2 (generic function with 1 method)

julia> tryeval2()
2

Finally, let's take a look at some more complex examples where this rule comes into play.

.. doctest::

julia> # initially f(x) has one definition:

julia> f(x) = "original definition";

julia> # start some other operations that use f(x):

julia> g(x) = f(x);

julia> t = @async f(wait()); yield();

julia> @generated gen1(x) = f(x);

julia> @generated gen2(x) = :(f(x));

julia> # now we add some new definitions for f(x):

julia> f(x::Int) = "definition for Int";

julia> f(x::Type{Int}) = "definition for Type{Int}";

julia> # and compare how these results differ:

julia> f(1)
"definition for Int"

julia> g(1)
"definition for Int"

julia> wait(schedule(t, 1))
"original definition"

julia> t = @async f(wait()); yield();

julia> wait(schedule(t, 1))
"definition for Int"

julia> gen1(1)
"original definition"

julia> gen2(1)
"definition for Int"

julia> # each method of a generated function has its own view of defined functions:

julia> @generated gen1(x::Real) = f(x);

julia> gen1(1)
"definition for Type{Int}"


Method Ambiguities
------------------

Expand Down

0 comments on commit 32709f4

Please sign in to comment.