summary refs log tree commit diff stats
path: root/doc/tut2.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tut2.txt')
-rw-r--r--doc/tut2.txt160
1 files changed, 62 insertions, 98 deletions
diff --git a/doc/tut2.txt b/doc/tut2.txt
index dc14aabc0..0bea28a8f 100644
--- a/doc/tut2.txt
+++ b/doc/tut2.txt
@@ -33,7 +33,8 @@ Object Oriented Programming
 While Nimrod's support for object oriented programming (OOP) is minimalistic, 
 powerful OOP technics can be used. OOP is seen as *one* way to design a 
 program, not *the only* way. Often a procedural approach leads to simpler
-and more efficient code.
+and more efficient code. In particular, prefering aggregation over inheritance
+often yields to a better design.
 
 
 Objects
@@ -196,8 +197,8 @@ for any type:
   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. (That 
-would not even be lying: *pure OO* has no meaning anyway. :-)
+``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
+that's not true. :-)
 
 
 Properties
@@ -237,15 +238,15 @@ The ``[]`` array access operator can be overloaded to provide
   type
     TVector* = object
       x, y, z: float
-

+
   proc `[]=`* (v: var TVector, i: int, value: float) =
-    # setter

+    # setter
     case i
     of 0: v.x = value
     of 1: v.y = value
     of 2: v.z = value
     else: assert(false)
-

+
   proc `[]`* (v: TVector, i: int): float = 
     # getter
     case i
@@ -253,25 +254,29 @@ The ``[]`` array access operator can be overloaded to provide
     of 1: result = v.y
     of 2: result = v.z
     else: assert(false)
-   

+   
 The example is silly, since a vector is better modelled by a tuple which 
 already provides ``v[]`` access.
 
 
-Dynamic binding
----------------
-In Nimrod procedural types are used to implement dynamic binding. 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.
+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.
 
 .. code-block:: nimrod
   type
-    TFigure = object of TObject    # abstract base class:
-      draw: proc (my: var TFigure) # concrete classes implement this proc
+    TFigure = object of TObject     # abstract base class:
+      fDraw: proc (my: var TFigure) # concrete classes implement this proc
     
   proc init(f: var TFigure) = 
-    f.draw = nil
+    f.fDraw = nil
+  
+  proc draw(f: var TFigure) =
+    # ``draw`` dispatches dynamically:
+    f.fDraw(f)
   
   type
     TCircle = object of TFigure
@@ -282,7 +287,7 @@ provides a constructor, etc.
   proc init(my: var TCircle) = 
     init(TFigure(my)) # call base constructor
     my.radius = 5
-    my.draw = drawCircle
+    my.fdraw = drawCircle
 
   type
     TRectangle = object of TFigure
@@ -294,50 +299,7 @@ provides a constructor, etc.
     init(TFigure(my)) # call base constructor
     my.width = 5
     my.height = 10
-    my.draw = drawRectangle
-
-  # now use these classes:
-  var
-    r: TRectangle
-    c: TCircle
-  init(r)
-  init(c)
-  r.draw(r)
-  c.draw(c) 
-
-The last line shows the syntactical difference between static and dynamic 
-binding: The ``r.draw(r)`` dynamic call refers to ``r`` twice. This difference
-is not necessarily bad. But if you want to eliminate the somewhat redundant
-``r``, it can be done by using *closures*: 
-
-.. code-block:: nimrod
-  type
-    TFigure = object of TObject    # abstract base class:
-      draw: proc () {.closure.}    # concrete classes implement this proc
-    
-  proc init(f: var TFigure) = 
-    f.draw = nil
-  
-  type
-    TCircle = object of TFigure
-      radius: int
-  
-  proc init(me: var TCircle) = 
-    init(TFigure(me)) # call base constructor
-    me.radius = 5
-    me.draw = lambda () = 
-      echo("o " & $me.radius)
-
-  type
-    TRectangle = object of TFigure
-      width, height: int
-  
-  proc init(me: var TRectangle) = 
-    init(TFigure(me)) # call base constructor
-    me.width = 5
-    me.height = 10
-    me.draw = lambda () =
-      echo("[]")
+    my.fdraw = drawRectangle
 
   # now use these classes:
   var
@@ -348,10 +310,12 @@ is not necessarily bad. But if you want to eliminate the somewhat redundant
   r.draw()
   c.draw() 
 
-The example also introduces `lambda`:idx: expressions: A ``lambda`` expression
-defines a new proc with the ``closure`` calling convention on the fly.
+The code uses a ``draw`` procedure that is bound statically, but inside it
+the dynamic dispatch happens with the help of the ``fdraw`` field. This is
+slightly more inconvienent than in traditional OOP-languages, but has the
+advantage of being much more flexible (and somewhat faster). The above approach
+also allows some form *monkey patching* by modifying the ``fdraw`` field.
 
-`Version 0.7.4: Closures and lambda expressions are not implemented.`:red:
 
 
 Exceptions
@@ -432,7 +396,7 @@ is not executed (if an exception occurs).
 Generics
 ========
 
-`Version 0.7.4: Complex generic types like in the example do not work.`:red:
+`Version 0.7.6: Complex generic types like in the example do not work.`:red:
 
 `Generics`:idx: are Nimrod's means to parametrize procs, iterators or types 
 with `type parameters`:idx:. They are most useful for efficient type safe
@@ -458,8 +422,8 @@ containers:
     else:
       var it = root
       while it != nil:
-        # compare the data items; uses the generic ``cmd`` proc that works for
-        # any type that has a ``==`` and ``<`` operator
+        # compare the data items; uses the generic ``cmd`` proc
+        # that works for any type that has a ``==`` and ``<`` operator
         var c = cmp(it.data, n.data) 
         if c < 0:
           if it.le == nil:
@@ -491,7 +455,7 @@ containers:
       
   var
     root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
-  add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and ``add``
+  add(root, newNode("hallo")) # instantiates ``newNode`` and ``add``
   add(root, "world")          # instantiates the second ``add`` proc
   for str in preorder(root):
     stdout.writeln(str)
@@ -567,6 +531,7 @@ Turning the ``log`` proc into a template solves this problem in an elegant way:
 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.
+In later versions, real types will be supported too.
 
 The template body does not open a new scope. To open a new scope
 use a ``block`` statement:
@@ -587,6 +552,35 @@ use a ``block`` statement:
   b = 42  # does not work, `b` is unknown
 
 
+If there is a ``stmt`` parameter it should be the last in the template
+declaration. The reason is that statements can be passed to a template
+via a special ``:`` syntax:
+
+.. code-block:: nimrod
+
+  template withFile(f, filename, mode: expr, actions: stmt): stmt =
+    block:
+      var fn = filename
+      var f: TFile
+      if openFile(f, fn, mode):
+        try:
+          actions
+        finally:
+          closeFile(f)
+      else:
+        quit("cannot open: " & fn)
+      
+  withFile(txt, "ttempl3.txt", fmWrite):
+    txt.writeln("line 1")
+    txt.writeln("line 2")
+  
+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. 
+
+
 Macros
 ======
 
@@ -658,36 +652,6 @@ The macro call expands to:
   writeln(stdout, x)
 
 
-Lets return to the dynamic binding ``r.draw(r)`` notational "problem". Apart 
-from closures, there is another "solution": Define an infix ``!`` macro 
-operator which hides it: 
-
-.. code-block:: 
-
-  macro `!` (n: expr): expr = 
-    result = newNimNode(nnkCall, n)
-    var dot = newNimNode(nnkDotExpr, n)
-    dot.add(n[1])    # obj
-    if n[2].kind == nnkCall:
-      # transforms ``obj!method(arg1, arg2, ...)`` to
-      # ``(obj.method)(obj, arg1, arg2, ...)``
-      dot.add(n[2][0]) # method
-      result.add(dot)
-      result.add(n[1]) # obj
-      for i in 1..n[2].len-1:
-        result.add(n[2][i])
-    else:
-      # transforms ``obj!method`` to
-      # ``(obj.method)(obj)``
-      dot.add(n[2]) # method
-      result.add(dot)
-      result.add(n[1]) # obj
-  
-  r!draw(a, b, c) # will be transfomed into ``r.draw(r, a, b, c)``
-
-Great! 20 lines of complex code to safe a few keystrokes! Obviously, this is
-exactly you should not do! (But it makes a cool example.)
-
 
 Statement Macros
 ----------------