implemented multi methods
This commit is contained in:
parent
66a7e3d37c
commit
3f3dda5a77
65 changed files with 11086 additions and 1258 deletions
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
.. contents::
|
||||
|
||||
Abstraction is layering ignorance on top of reality. -- unknown
|
||||
|
||||
|
||||
Directory structure
|
||||
===================
|
||||
|
|
@ -72,18 +74,6 @@ the same::
|
|||
./boot [-d:release]
|
||||
|
||||
|
||||
Coding Guidelines
|
||||
=================
|
||||
|
||||
* Use CamelCase, not underscored_identifiers.
|
||||
* Indent with two spaces.
|
||||
* Max line length is 80 characters.
|
||||
* Provide spaces around binary operators if that enhances readability.
|
||||
* Use a space after a colon, but not before it.
|
||||
* Start types with a capital ``T``, unless they are pointers which start with
|
||||
``P``.
|
||||
|
||||
|
||||
Pascal annotations
|
||||
==================
|
||||
There are some annotations that the Pascal sources use so that they can
|
||||
|
|
|
|||
118
doc/lib.txt
118
doc/lib.txt
|
|
@ -5,6 +5,10 @@ Nimrod Standard Library
|
|||
:Author: Andreas Rumpf
|
||||
:Version: |nimrodversion|
|
||||
|
||||
..
|
||||
|
||||
The good thing about reinventing the wheel is that you can get a round one.
|
||||
|
||||
Though the Nimrod Standard Library is still evolving, it is already quite
|
||||
usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
|
||||
|
||||
|
|
@ -12,10 +16,15 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
|
|||
while impure libraries do. A wrapper is an impure library that is a very
|
||||
low-level interface to a C library.
|
||||
|
||||
Read this `document <apis.html>`_ for a quick overview of the API design.
|
||||
|
||||
|
||||
Pure libraries
|
||||
==============
|
||||
|
||||
Core
|
||||
----
|
||||
|
||||
* `system <system.html>`_
|
||||
Basic procs and operators that every program needs. It also provides IO
|
||||
facilities for reading and writing text and binary files. It is imported
|
||||
|
|
@ -25,11 +34,35 @@ Pure libraries
|
|||
* `macros <macros.html>`_
|
||||
Contains the AST API and documentation of Nimrod for writing macros.
|
||||
|
||||
|
||||
String handling
|
||||
---------------
|
||||
|
||||
* `strutils <strutils.html>`_
|
||||
This module contains common string handling operations like converting a
|
||||
string into uppercase, splitting a string into substrings, searching for
|
||||
substrings, replacing substrings.
|
||||
|
||||
* `strtabs <strtabs.html>`_
|
||||
The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||
from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||
style-insensitive mode. An efficient string substitution operator ``%``
|
||||
for the string table is also provided.
|
||||
|
||||
* `unicode <unicode.html>`_
|
||||
This module provides support to handle the Unicode UTF-8 encoding.
|
||||
|
||||
* `regexprs <regexprs.html>`_
|
||||
This module contains procedures and operators for handling regular
|
||||
expressions. Consider using `pegs` instead.
|
||||
|
||||
* `pegs <pegs.html>`_
|
||||
This module contains procedures and operators for handling PEGs.
|
||||
|
||||
|
||||
Generic Operating System Services
|
||||
---------------------------------
|
||||
|
||||
* `os <os.html>`_
|
||||
Basic operating system facilities like retrieving environment variables,
|
||||
reading command line arguments, working with directories, running shell
|
||||
|
|
@ -37,7 +70,28 @@ Pure libraries
|
|||
platform independant.
|
||||
|
||||
* `osproc <osproc.html>`_
|
||||
Module for process communication beyond ``os.executeShellCommand``.
|
||||
Module for process communication beyond ``os.execShellCmd``.
|
||||
|
||||
* `times <times.html>`_
|
||||
The ``times`` module contains basic support for working with time.
|
||||
|
||||
* `dynlib <dynlib.html>`_
|
||||
This module implements the ability to access symbols from shared libraries.
|
||||
|
||||
* `streams <streams.html>`_
|
||||
This module provides a stream interface and two implementations thereof:
|
||||
the `PFileStream` and the `PStringStream` which implement the stream
|
||||
interface for Nimrod file objects (`TFile`) and strings. Other modules
|
||||
may provide other implementations for this standard stream interface.
|
||||
|
||||
* `terminal <terminal.html>`_
|
||||
This module contains a few procedures to control the *terminal*
|
||||
(also called *console*). The implementation simply uses ANSI escape
|
||||
sequences and does not depend on any other module.
|
||||
|
||||
|
||||
Math libraries
|
||||
--------------
|
||||
|
||||
* `math <math.html>`_
|
||||
Mathematical operations like cosine, square root.
|
||||
|
|
@ -45,11 +99,17 @@ Pure libraries
|
|||
* `complex <complex.html>`_
|
||||
This module implements complex numbers and their mathematical operations.
|
||||
|
||||
* `times <times.html>`_
|
||||
The ``times`` module contains basic support for working with time.
|
||||
|
||||
* `dynlib <dynlib.html>`_
|
||||
This module implements the ability to access symbols from shared libraries.
|
||||
|
||||
Internet Protocols and Support
|
||||
------------------------------
|
||||
|
||||
* `cgi <cgi.html>`_
|
||||
This module implements helpers for CGI applictions.
|
||||
|
||||
|
||||
Parsers
|
||||
-------
|
||||
|
||||
* `parseopt <parseopt.html>`_
|
||||
The ``parseopt`` module implements a command line option parser. This
|
||||
|
|
@ -71,48 +131,32 @@ Pure libraries
|
|||
|
||||
* `parsecsv <parsecsv.html>`_
|
||||
The ``parsecsv`` module implements a simple high performance CSV parser.
|
||||
|
||||
* `parsesql <parsesql.html>`_
|
||||
The ``parsesql`` module implements a simple high performance SQL parser.
|
||||
|
||||
* `strtabs <strtabs.html>`_
|
||||
The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||
from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||
style-insensitive mode. An efficient string substitution operator ``%``
|
||||
for the string table is also provided.
|
||||
* `lexbase <lexbase.html>`_
|
||||
This is a low level module that implements an extremely efficient buffering
|
||||
scheme for lexers and parsers. This is used by the diverse parsing modules.
|
||||
|
||||
* `streams <streams.html>`_
|
||||
This module provides a stream interface and two implementations thereof:
|
||||
the `PFileStream` and the `PStringStream` which implement the stream
|
||||
interface for Nimrod file objects (`TFile`) and strings. Other modules
|
||||
may provide other implementations for this standard stream interface.
|
||||
|
||||
Code generation
|
||||
---------------
|
||||
|
||||
* `xmlgen <xmlgen.html>`_
|
||||
This module implements macros for XML/HTML code generation.
|
||||
|
||||
|
||||
Cryptography and Hashing
|
||||
------------------------
|
||||
|
||||
* `hashes <hashes.html>`_
|
||||
This module implements efficient computations of hash values for diverse
|
||||
Nimrod types.
|
||||
|
||||
* `lexbase <lexbase.html>`_
|
||||
This is a low level module that implements an extremely efficient buffering
|
||||
scheme for lexers and parsers. This is used by the ``parsecfg`` module.
|
||||
|
||||
* `terminal <terminal.html>`_
|
||||
This module contains a few procedures to control the *terminal*
|
||||
(also called *console*). The implementation simply uses ANSI escape
|
||||
sequences and does not depend on any other module.
|
||||
|
||||
* `cgi <cgi.html>`_
|
||||
This module implements helper procs for CGI applictions.
|
||||
|
||||
* `unicode <unicode.html>`_
|
||||
This module provides support to handle the Unicode UTF-8 encoding.
|
||||
|
||||
* `regexprs <regexprs.html>`_
|
||||
This module contains procedures and operators for handling regular
|
||||
expressions.
|
||||
|
||||
* `md5 <md5.html>`_
|
||||
This module implements the MD5 checksum algorithm.
|
||||
|
||||
* `xmlgen <xmlgen.html>`_
|
||||
This module implements macros for HTML code generation.
|
||||
|
||||
|
||||
|
||||
Impure libraries
|
||||
|
|
|
|||
|
|
@ -1357,7 +1357,7 @@ given *slicelist* the ``else`` part is executed. If there is no ``else``
|
|||
part and not all possible values that ``expr`` can hold occur in a ``vallist``,
|
||||
a static error is given. This holds only for expressions of ordinal types.
|
||||
If the expression is not of an ordinal type, and no ``else`` part is
|
||||
given, control just passes after the ``case`` statement.
|
||||
given, control passes after the ``case`` statement.
|
||||
|
||||
To suppress the static error in the ordinal case an ``else`` part with a ``nil``
|
||||
statement can be used.
|
||||
|
|
@ -1604,7 +1604,9 @@ statement is syntactic sugar for a nested block:
|
|||
continue
|
||||
stmt2
|
||||
|
||||
# is equivalent to:
|
||||
Is equivalent to:
|
||||
|
||||
.. code-block:: nimrod
|
||||
while expr1:
|
||||
block myBlockName:
|
||||
stmt1
|
||||
|
|
@ -1669,7 +1671,7 @@ object on the stack and can thus reference a non-existing object.
|
|||
|
||||
Procedures
|
||||
~~~~~~~~~~
|
||||
What most programming languages call `methods`:idx: or `funtions`:idx: are
|
||||
What most programming languages call `methods`:idx: or `functions`:idx: are
|
||||
called `procedures`:idx: in Nimrod (which is the correct terminology). A
|
||||
procedure declaration defines an identifier and associates it with a block
|
||||
of code. A procedure may call itself recursively. The syntax is::
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ that its extension should be ``.cfg``.
|
|||
Command line settings have priority over configuration file settings.
|
||||
|
||||
|
||||
Nimrod's directory structure
|
||||
----------------------------
|
||||
Generated C code directory
|
||||
--------------------------
|
||||
The generated files that Nimrod produces all go into a subdirectory called
|
||||
``nimcache`` in your project directory. This makes it easy to delete all
|
||||
generated files.
|
||||
|
|
@ -116,7 +116,7 @@ No_decl Pragma
|
|||
The `no_decl`:idx: pragma can be applied to almost any symbol (variable, proc,
|
||||
type, etc.) and is sometimes useful for interoperability with C:
|
||||
It tells Nimrod that it should not generate a declaration for the symbol in
|
||||
the C code. Thus it makes the following possible, for example:
|
||||
the C code. For example:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var
|
||||
|
|
@ -129,12 +129,12 @@ However, the ``header`` pragma is often the better alternative.
|
|||
Header Pragma
|
||||
~~~~~~~~~~~~~
|
||||
The `header`:idx: pragma is very similar to the ``no_decl`` pragma: It can be
|
||||
applied to almost any symbol and specifies that not only it should not be
|
||||
declared but also that it leads to the inclusion of a given header file:
|
||||
applied to almost any symbol and specifies that it should not be declared
|
||||
and instead the generated code should contain an ``#include``:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
type
|
||||
PFile {.importc: "FILE*", header: "<stdio.h>".} = pointer
|
||||
PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
|
||||
# import C's FILE* type; Nimrod will treat it as a new pointer type
|
||||
|
||||
The ``header`` pragma expects always a string constant. The string contant
|
||||
|
|
@ -159,7 +159,7 @@ strings automatically:
|
|||
Line_dir Option
|
||||
~~~~~~~~~~~~~~~
|
||||
The `line_dir`:idx: option can be turned on or off. If on the generated C code
|
||||
contains ``#line`` directives.
|
||||
contains ``#line`` directives. This may be helpful for debugging with GDB.
|
||||
|
||||
|
||||
Stack_trace Option
|
||||
|
|
@ -198,7 +198,7 @@ Register Pragma
|
|||
The `register`:idx: pragma is for variables only. It declares the variable as
|
||||
``register``, giving the compiler a hint that the variable should be placed
|
||||
in a hardware register for faster access. C compilers usually ignore this
|
||||
though and for good reason: Often they do a better job without it anyway.
|
||||
though and for good reasons: Often they do a better job without it anyway.
|
||||
|
||||
In highly specific cases (a dispatch loop of an bytecode interpreter for
|
||||
example) it may provide benefits, though.
|
||||
|
|
@ -228,7 +228,7 @@ memory, but nothing worse happens.
|
|||
Dead_code_elim Pragma
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The `dead_code_elim`:idx: pragma only applies to whole modules: It tells the
|
||||
compiler to active (or deactivate) dead code elimination for the module the
|
||||
compiler to activate (or deactivate) dead code elimination for the module the
|
||||
pragma appers in.
|
||||
|
||||
The ``--dead_code_elim:on`` command line switch has the same effect as marking
|
||||
|
|
@ -288,9 +288,9 @@ Optimizing string handling
|
|||
String assignments are sometimes expensive in Nimrod: They are required to
|
||||
copy the whole string. However, the compiler is often smart enough to not copy
|
||||
strings. Due to the argument passing semantics, strings are never copied when
|
||||
passed to subroutines. The compiler does not copy strings that are returned by
|
||||
a routine, because a routine returns a new string anyway. Thus it is efficient
|
||||
to do:
|
||||
passed to subroutines. The compiler does not copy strings that are result from
|
||||
a procedure call, because the called procedure returns a new string anyway.
|
||||
Thus it is efficient to do:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var s = procA() # assignment will not copy the string; procA allocates a new
|
||||
|
|
|
|||
1051
doc/theindex.txt
1051
doc/theindex.txt
File diff suppressed because it is too large
Load diff
38
doc/tut1.txt
38
doc/tut1.txt
|
|
@ -1,6 +1,6 @@
|
|||
============================
|
||||
The Nimrod Tutorial (Part I)
|
||||
============================
|
||||
========================
|
||||
Nimrod Tutorial (Part I)
|
||||
========================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimrodversion|
|
||||
|
|
@ -63,7 +63,8 @@ Nimrod: It is a good compromise between brevity and readability.
|
|||
|
||||
The "hallo world" program contains several identifiers that are already
|
||||
known to the compiler: ``echo``, ``readLine``, etc. These built-in items are
|
||||
declared in the system_ module which is implicitly imported by any other module.
|
||||
declared in the system_ module which is implicitly imported by any other
|
||||
module.
|
||||
|
||||
|
||||
Lexical elements
|
||||
|
|
@ -173,7 +174,7 @@ to a storage location:
|
|||
var x = "abc" # introduces a new variable `x` and assigns a value to it
|
||||
x = "xyz" # assigns a new value to `x`
|
||||
|
||||
The ``=`` is called the *assignment operator*. The assignment operator cannot
|
||||
``=`` is the *assignment operator*. The assignment operator cannot
|
||||
be overloaded, overwritten or forbidden, but this might change in a future
|
||||
version of Nimrod.
|
||||
|
||||
|
|
@ -196,7 +197,7 @@ constants:
|
|||
x = 1
|
||||
# a comment can occur here too
|
||||
y = 2
|
||||
z = y + 5 # simple computations are possible
|
||||
z = y + 5 # computations are possible
|
||||
|
||||
|
||||
Control flow statements
|
||||
|
|
@ -277,14 +278,13 @@ the compiler that for every other value nothing should be done:
|
|||
The ``nil`` statement is a *do nothing* statement. The compiler knows that a
|
||||
case statement with an else part cannot fail and thus the error disappers. Note
|
||||
that it is impossible to cover any possible string value: That is why there is
|
||||
no such compiler check for string cases.
|
||||
no such check for string cases.
|
||||
|
||||
In general the case statement is used for subrange types or enumerations where
|
||||
it is of great help that the compiler checks that you covered any possible
|
||||
value.
|
||||
|
||||
|
||||
|
||||
While statement
|
||||
---------------
|
||||
|
||||
|
|
@ -327,7 +327,7 @@ the same:
|
|||
Echo($i)
|
||||
inc(i) # increment i by 1
|
||||
|
||||
Counting down can be achieved as easily (but is much less needed):
|
||||
Counting down can be achieved as easily (but is less often needed):
|
||||
|
||||
.. code-block:: nimrod
|
||||
Echo("Counting down from 10 to 1: ")
|
||||
|
|
@ -372,7 +372,7 @@ Break statement
|
|||
---------------
|
||||
A block can be left prematurely with a ``break`` statement. The break statement
|
||||
can leave a while, for, or a block statement. It leaves the innermost construct,
|
||||
unless the label of a block is given:
|
||||
unless a label of a block is given:
|
||||
|
||||
.. code-block:: nimrod
|
||||
block myblock:
|
||||
|
|
@ -392,7 +392,7 @@ unless the label of a block is given:
|
|||
|
||||
Continue statement
|
||||
------------------
|
||||
Like in many other programming languages, a ``continue`` statement leads to
|
||||
Like in many other programming languages, a ``continue`` statement starts
|
||||
the next iteration immediately:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
|
@ -512,7 +512,7 @@ the procedure (and therefore the while loop) immediately. The
|
|||
parameter named ``question`` of type ``string`` and returns a value of type
|
||||
``bool``. ``Bool`` is a built-in type: The only valid values for ``bool`` are
|
||||
``true`` and ``false``.
|
||||
The conditions in if or while statements need to have the type ``bool``.
|
||||
The conditions in if or while statements should be of the type ``bool``.
|
||||
|
||||
Some terminology: In the example ``question`` is called a (formal) *parameter*,
|
||||
``"Should I..."`` is called an *argument* that is passed to this parameter.
|
||||
|
|
@ -614,7 +614,7 @@ Now the call to ``createWindow`` only needs to set the values that differ
|
|||
from the defaults.
|
||||
|
||||
Note that type inference works for parameters with default values, there is
|
||||
no need to specify ``title: string = "unknown"``, for example.
|
||||
no need to write ``title: string = "unknown"``, for example.
|
||||
|
||||
|
||||
Overloaded procedures
|
||||
|
|
@ -857,7 +857,6 @@ loses information, the `EOutOfRange`:idx: exception is raised (if the error
|
|||
cannot be detected at compile time).
|
||||
|
||||
|
||||
|
||||
Floats
|
||||
------
|
||||
Nimrod has these floating point types built-in: ``float float32 float64``.
|
||||
|
|
@ -1232,7 +1231,7 @@ mysterious crashes.
|
|||
**Note**: The example only works because the memory is initialized with zero
|
||||
(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
|
||||
``nil`` which the string assignment can handle. You need to know low level
|
||||
details like this when mixing garbage collected data with unmanaged memory!
|
||||
details like this when mixing garbage collected data with unmanaged memory.
|
||||
|
||||
|
||||
Procedural type
|
||||
|
|
@ -1240,8 +1239,7 @@ Procedural type
|
|||
A `procedural type`:idx: is a (somewhat abstract) pointer to a procedure.
|
||||
``nil`` is an allowed value for a variable of a procedural type.
|
||||
Nimrod uses procedural types to achieve `functional`:idx: programming
|
||||
techniques. Dynamic dispatch for OOP constructs can also be implemented with
|
||||
procedural types (details follow in the OOP section).
|
||||
techniques.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1303,9 +1301,9 @@ because then one module cannot be reused without the other.
|
|||
|
||||
The algorithm for compiling modules is:
|
||||
|
||||
- Compile the whole module as usual, following import statements recursively
|
||||
- if there is a cycle only import the already parsed symbols (that are
|
||||
exported); if an unknown identifier occurs then abort
|
||||
- Compile the whole module as usual, following import statements recursively.
|
||||
- If there is a cycle only import the already parsed symbols (that are
|
||||
exported); if an unknown identifier occurs then abort.
|
||||
|
||||
This is best illustrated by an example:
|
||||
|
||||
|
|
|
|||
176
doc/tut2.txt
176
doc/tut2.txt
|
|
@ -1,6 +1,6 @@
|
|||
=============================
|
||||
The Nimrod Tutorial (Part II)
|
||||
=============================
|
||||
=========================
|
||||
Nimrod Tutorial (Part II)
|
||||
=========================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimrodversion|
|
||||
|
|
@ -73,9 +73,9 @@ section.
|
|||
|
||||
Inheritance is done with the ``object of`` syntax. Multiple inheritance is
|
||||
currently not supported. If an object type has no suitable ancestor, ``TObject``
|
||||
should be used as its ancestor, but this is only a convention.
|
||||
can be used as its ancestor, but this is only a convention.
|
||||
|
||||
Note that aggregation (*has-a* relation) is often preferable to inheritance
|
||||
**Note**: Aggregation (*has-a* relation) is often preferable to inheritance
|
||||
(*is-a* relation) for simple code reuse. Since objects are value types in
|
||||
Nimrod, aggregation is as efficient as inheritance.
|
||||
|
||||
|
|
@ -84,9 +84,9 @@ Mutually recursive types
|
|||
------------------------
|
||||
|
||||
Objects, tuples and references can model quite complex data structures which
|
||||
depend on each other. This is called *mutually recursive types*. In Nimrod
|
||||
these types need to be declared within a single type section. Anything else
|
||||
would require arbitrary symbol lookahead which slows down compilation.
|
||||
depend on each other; they are *mutually recursive*. In Nimrod
|
||||
these types can only be declared within a single type section. (Anything else
|
||||
would require arbitrary symbol lookahead which slows down compilation.)
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ An example:
|
|||
TNode = object
|
||||
case kind: TNodeKind # the ``kind`` field is the discriminator
|
||||
of nkInt: intVal: int
|
||||
of nkFloat: floavVal: float
|
||||
of nkFloat: floatVal: float
|
||||
of nkString: strVal: string
|
||||
of nkAdd, nkSub:
|
||||
leftOp, rightOp: PNode
|
||||
|
|
@ -176,18 +176,25 @@ bound to a class. This has disadvantages:
|
|||
|
||||
* Adding a method to a class the programmer has no control over is
|
||||
impossible or needs ugly workarounds.
|
||||
* Often it is unclear where the procedure should belong to: Is
|
||||
* Often it is unclear where the method should belong to: Is
|
||||
``join`` a string method or an array method? Should the complex
|
||||
``vertexCover`` algorithm really be a method of the ``graph`` class?
|
||||
|
||||
Nimrod avoids these problems by not distinguishing between methods and
|
||||
procedures. Methods are just ordinary procedures. However, there is a special
|
||||
syntactic sugar for calling procedures: The syntax ``obj.method(args)`` can be
|
||||
used instead of ``method(obj, args)``. If there are no remaining arguments, the
|
||||
parentheses can be omitted: ``obj.len`` (instead of ``len(obj)``).
|
||||
Nimrod avoids these problems by not assigning methods to a class. All methods
|
||||
in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are
|
||||
distinguished from procs only for dynamic binding purposes.
|
||||
|
||||
|
||||
Method call syntax
|
||||
------------------
|
||||
|
||||
There is a syntactic sugar for calling routines:
|
||||
The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``.
|
||||
If there are no remaining arguments, the parentheses can be omitted:
|
||||
``obj.len`` (instead of ``len(obj)``).
|
||||
|
||||
This `method call syntax`:idx: is not restricted to objects, it can be used
|
||||
for any type:
|
||||
for any type:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
|
|
@ -196,9 +203,17 @@ for any type:
|
|||
echo({'a', 'b', 'c'}.card)
|
||||
stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
|
||||
|
||||
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of
|
||||
``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
|
||||
that's not true. :-)
|
||||
(Another way to look at the method call syntax is that it provides the missing
|
||||
postfix notation.)
|
||||
|
||||
So code that looks "pure object oriented" is easy to write:
|
||||
|
||||
.. code-block:: nimrod
|
||||
import strutils
|
||||
|
||||
stdout.writeln("Give a list of numbers (separated by spaces): ")
|
||||
stdout.write(stdin.readLine.split.each(parseInt).max.`$`)
|
||||
stdout.writeln(" is the maximum!")
|
||||
|
||||
|
||||
Properties
|
||||
|
|
@ -261,60 +276,75 @@ already provides ``v[]`` access.
|
|||
|
||||
Dynamic dispatch
|
||||
----------------
|
||||
In Nimrod procedural types are used to implement dynamic dispatch. The
|
||||
following example also shows some more conventions: The ``self`` or ``this``
|
||||
object is named ``my`` (because it is shorter than the alternatives), each
|
||||
class provides a constructor, etc.
|
||||
|
||||
Procedures always use static dispatch. To get dynamic dispatch, replace the
|
||||
``proc`` keyword by ``method``:
|
||||
|
||||
.. code-block:: nimrod
|
||||
type
|
||||
TFigure = object of TObject # abstract base class:
|
||||
fDraw: proc (my: var TFigure) # concrete classes implement this proc
|
||||
TExpr = object ## abstract base class for an expression
|
||||
TLiteral = object of TExpr
|
||||
x: int
|
||||
TPlusExpr = object of TExpr
|
||||
a, b: ref TExpr
|
||||
|
||||
method eval(e: ref TExpr): int =
|
||||
# override this base method
|
||||
quit "to override!"
|
||||
|
||||
method eval(e: ref TLiteral): int = return e.x
|
||||
|
||||
method eval(e: ref TPlusExpr): int =
|
||||
# watch out: relies on dynamic binding
|
||||
return eval(e.a) + eval(e.b)
|
||||
|
||||
proc newLit(x: int): ref TLiteral =
|
||||
new(result)
|
||||
result.x = x
|
||||
|
||||
proc init(f: var TFigure) =
|
||||
f.fDraw = nil
|
||||
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
|
||||
new(result)
|
||||
result.a = a
|
||||
result.b = b
|
||||
|
||||
proc draw(f: var TFigure) =
|
||||
# ``draw`` dispatches dynamically:
|
||||
f.fDraw(f)
|
||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||
|
||||
type
|
||||
TCircle = object of TFigure
|
||||
radius: int
|
||||
|
||||
proc drawCircle(my: var TCircle) = echo("o " & $my.radius)
|
||||
|
||||
proc init(my: var TCircle) =
|
||||
init(TFigure(my)) # call base constructor
|
||||
my.radius = 5
|
||||
my.fdraw = drawCircle
|
||||
Note that in the example the constructors ``newLit`` and ``newPlus`` are procs
|
||||
because they should use static binding, but ``eval`` is a method because it
|
||||
requires dynamic binding.
|
||||
|
||||
In a multi-method all parameters that have an object type are used for the
|
||||
dispatching:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
type
|
||||
TRectangle = object of TFigure
|
||||
width, height: int
|
||||
TThing = object
|
||||
TUnit = object of TThing
|
||||
x: int
|
||||
|
||||
method collide(a, b: TThing) {.inline.} =
|
||||
quit "to override!"
|
||||
|
||||
method collide(a: TThing, b: TUnit) {.inline.} =
|
||||
echo "1"
|
||||
|
||||
proc drawRectangle(my: var TRectangle) = echo("[]")
|
||||
method collide(a: TUnit, b: TThing) {.inline.} =
|
||||
echo "2"
|
||||
|
||||
proc init(my: var TRectangle) =
|
||||
init(TFigure(my)) # call base constructor
|
||||
my.width = 5
|
||||
my.height = 10
|
||||
my.fdraw = drawRectangle
|
||||
|
||||
# now use these classes:
|
||||
var
|
||||
r: TRectangle
|
||||
c: TCircle
|
||||
init(r)
|
||||
init(c)
|
||||
r.draw()
|
||||
c.draw()
|
||||
a, b: TUnit
|
||||
collide(a, b) # output: 2
|
||||
|
||||
The code uses a ``draw`` procedure that is bound statically, but inside it
|
||||
the dynamic dispatch happens with the help of the ``fdraw`` field. Even though
|
||||
this solution has its advantages compared to traditional OOP-languages, it is
|
||||
a **preliminary** solution. Multimethods are a planned language feature that
|
||||
provide a more flexible and efficient mechanism.
|
||||
|
||||
As the example demonstrates, invokation of a multi-method cannot be ambiguous:
|
||||
Collide 2 is prefered over collide 1 because the resolution works from left to
|
||||
right. Thus ``TUnit, TThing`` is prefered over ``TThing, TUnit``.
|
||||
|
||||
**Perfomance note**: Nimrod does not produce a virtual method table, but
|
||||
generates dispatch trees. This avoids the expensive indirect branch for method
|
||||
calls and enables inlining. However, other optimizations like compile time
|
||||
evaluation or dead code elimination do not work with methods.
|
||||
|
||||
|
||||
Exceptions
|
||||
|
|
@ -497,8 +527,7 @@ simple proc for logging:
|
|||
debug = True
|
||||
|
||||
proc log(msg: string) {.inline.} =
|
||||
if debug:
|
||||
stdout.writeln(msg)
|
||||
if debug: stdout.writeln(msg)
|
||||
|
||||
var
|
||||
x = 4
|
||||
|
|
@ -506,7 +535,7 @@ simple proc for logging:
|
|||
|
||||
This code has a shortcoming: If ``debug`` is set to false someday, the quite
|
||||
expensive ``$`` and ``&`` operations are still performed! (The argument
|
||||
evaluation for procedures is said to be *eager*).
|
||||
evaluation for procedures is *eager*).
|
||||
|
||||
Turning the ``log`` proc into a template solves this problem:
|
||||
|
||||
|
|
@ -514,21 +543,20 @@ Turning the ``log`` proc into a template solves this problem:
|
|||
const
|
||||
debug = True
|
||||
|
||||
template log(msg: expr): stmt =
|
||||
if debug:
|
||||
stdout.writeln(msg)
|
||||
template log(msg: string) =
|
||||
if debug: stdout.writeln(msg)
|
||||
|
||||
var
|
||||
x = 4
|
||||
log("x has the value: " & $x)
|
||||
|
||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
||||
description*). These are no real types, they just help the compiler parsing.
|
||||
However, real types are supported too.
|
||||
The parameters' types can be ordinary types or the meta types ``expr``
|
||||
(stands for *expression*), ``stmt`` (stands for *statement*) or ``typedesc``
|
||||
(stands for *type description*). If the template has no explicit return type,
|
||||
``stmt`` is used for consistency with procs and methods.
|
||||
|
||||
The template body does not open a new scope. To open a new scope
|
||||
use a ``block`` statement:
|
||||
The template body does not open a new scope. To open a new scope use a ``block``
|
||||
statement:
|
||||
|
||||
.. code-block:: nimrod
|
||||
template declareInScope(x: expr, t: typeDesc): stmt =
|
||||
|
|
@ -573,7 +601,7 @@ In the example the two ``writeln`` statements are bound to the ``actions``
|
|||
parameter. The ``withFile`` template contains boilerplate code and helps to
|
||||
avoid a common bug: To forget to close the file. Note how the
|
||||
``var fn = filename`` statement ensures that ``filename`` is evaluated only
|
||||
once.
|
||||
once.
|
||||
|
||||
|
||||
Macros
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue