proto                 package:proto                 R Documentation

_P_r_o_t_o_t_y_p_e _o_b_j_e_c_t-_b_a_s_e_d _p_r_o_g_r_a_m_m_i_n_g

_D_e_s_c_r_i_p_t_i_o_n:

     'proto' creates or modifies objects of the proto object oriented
     system.

_U_s_a_g_e:

     proto(. = parent.env(envir), expr = {}, envir = 
                     new.env(parent = parent.frame()), ... )
     ## S3 method for class 'list':
     as.proto(x, envir, parent, FUN = function(x) TRUE, 
         all.names = FALSE, ...)
     isnot.function(x)

_A_r_g_u_m_e_n_t_s:

       .: the parent object of the new object.  May be a proto object
          or an environment.

    expr: a series of statements enclosed in braces that define the
          variables and methods of the object.  Empty braces, the
          default, may be used if there are no variables or methods to
          add at this time.

   envir: an existing prototype object or environment into which  the
          variables and methods defined in 'expr' are placed.   If
          omitted a new object is created.

       x: a list.

  parent: a prototype object or environment which is to be used as the
          parent of the object.  If 'envir' is specified then its
          parent is coerced to 'parent'.

     ...: for 'proto' these are components to be embedded in  the new
          object.  For 'as.proto.list' these are arguments to pass to
          'proto' in the case that a new object is created.

     FUN: only components for which FUN(x) is TRUE are copied.

all.names: only names not starting with a dot are copied unless
          all.names is TRUE.

_D_e_t_a_i_l_s:

     The 'proto' class is defined to be a subclass of the R
     'environment' class. In particular this implies that 'proto'
     objects have single inheritance and mutable state as all
     environments do. 'proto' creates or modifies objects of this
     class.  It inserts all variables and functions in 'expr' and then
     in '...' into 'envir' setting the parent to '.'.  The environment
     of all  functions inserted into the environment are set to that
     environment. All such functions should  have the receiver object
     as their first argument. Conventionally this is '.' (i.e. a dot). 
      Also '.that' and '.super' variables are added to the environment.
      These  point to the object itself and its parent. Note that
     'proto' can be used as a method and overridden like any other
     method.  This allows objects to have object-specific versions of
     'proto'.

     'as.proto' is a generic with methods for environments, proto
     objects and lists.

     'as.proto.list' copies inserts a copy of each component, 'el', of
     the list 'x' into the the environment or proto object 'envir' for
     which 'FUN(el)' is 'TRUE'.  Also, components whose name begins
     with a dot, '.', are not copied unless  'all.names' is 'TRUE' (and
     'FUN(el)' is 'TRUE'). The result is a proto object whose parent is
     'parent'. If 'envir' is omitted a new object is created through a
     call to 'proto' with 'parent' and '...' as arguments. If 'parent'
     is also omitted then the current environment is used. Note that if
     'parent' is a proto object with its own 'proto' method then this
     call to 'proto' will be overridden by that method.

     The utility function 'isnot.function' is provided for use with
     'as.proto.list' to facilitate the copying of variables only.

     '$' can be used to access or set variables and methods in an
     object.

     When '$' is used for getting variables and methods, calls of the
     form 'obj$v' search for v in 'obj' and if not found search upwards
     through the ancestors of 'obj' until found unless the name 'v'
     begins with two dots '..'.  In that case no upward search is done.

     If 'meth' is a function then 'obj$meth' should only be  used in
     the context of a call to a method, e.g. 'obj$meth(x,y)'. If it is
     desired to actually return the method as a value not in the
     context of a call then use the form  'obj$with(meth)' or
     'obj[[meth]]'  which are similar to 'with(obj, meth)' except that 
     the variation using 'with' will search through ancestors while
     '[[' will not search through ancestors). The difference between
     'obj$meth' and 'obj$with(meth)' is that in the first case 'obj'
     implicitly provides the first argument to the call so that
     'obj$meth(x,y)' and 'obj$with(meth)(obj,x,y)' are equivalent while
     in the case of 'obj$with(meth)' the first argument is not
     automatically inserted.  The forms '.that$meth' and  '.super$meth'
     are special and should only be used within methods.  '.that'
     refers to the object in which the current method is located and
     '.super' refers to the parent of '.that'.  In both cases the
     receiver object must be  specified as the first argument - the
     receiver is not automatically inserted as with other usages of
     '$'.

     '$' can be used to set variables and methods in an object. No
     ancestors are searched for the set form of '$'. If the variable is
     the special variable '.super' then not only is the variable set
     but the object's parent is set to '.super'.

     'is.proto(p)' returns TRUE if p is a prototype object.

_V_a_l_u_e:

     'proto' and 'as.proto' all return proto objects.  'isnot.function'
     returns a logical value.

_N_o_t_e:

     proto methods can be used with environments but some care must be
     taken.  These can be avoided by always using proto objects in
     these cases.  This note discusses the pitfalls of using
     environments for those cases where such interfacing is needed.

     Note that if 'e' is an environment then 'e$x' will only search for
     'x' in 'e' and no further whereas if 'e' were a proto object its
     ancestors will be searched as well. For example, if the parent of
     a 'proto' object is an 'environment' but not itself a 'proto'
     object then '.super$x' references in the methods of that object
     will only look as far as the parent.

     Also note that the form 'e$meth(...)' when used with an
     environment will not automatically insert 'e' as the first
     argument and so environments can only be used with methods by
     using the more verbose 'e$meth(e, ...)'.  Even then it is not
     exactly equivalent since 'meth' will only be looked up in 'e' but
     not its ancestors. To get precise equivalence write the even more
     verbose 'with(e, meth)(e, ...)'.

     If the user has a proto object 'obj' which is a child of the
     global environment and whose methods  use '.super' then '.super'
     will refer to an environment, not a proto object (unless the
     global environment is coerced to a proto object) and therefore be
     faced with the search situation discussed above.  One solution is
     to create an empty root object between the global environment and
     'obj' like this 'Root <- obj$.super <- proto(.GlobalEnv)' where
     'Root' is the root object. Now '.super' references will reference
     'Root', which is a proto object so search will occur as expected. 
     'proto' does not provide such a root object automatically but the
     user can create one easily as shown, if desired.

     Although not recommended, it possible to coerce the global
     environment to a proto object by issuing the command
     'as.proto(.GlobalEnv)'.  This will effectively make the global
     environment a proto root object but has the potential to break
     other software, although the  authors have not actually found any
     software that it breaks.

_S_e_e _A_l_s_o:

     'as.list', 'names', 'environment'

_E_x_a_m_p_l_e_s:

     oo <- proto(expr = {x = c(10, 20, 15, 19, 17)
                  location <- function(.) mean(.$x) # 1st arg is object
                  rms <- function(.) 
                           sqrt(mean((.$x - .$location())^2))
                  bias <- function(., b) .$x <- .$x + b
     })

     debug(oo$with(rms)) # cannot use oo$rms to pass method as a value
     undebug(oo$with(rms)) # cannot use oo$rms to pass method as a value

     oo2 <- oo$proto( location = function(.) median(.$x) )
     oo2$rms()      # note that first argument is omitted.
     oo2$ls()       # list components of oo2
     oo2$as.list()  # contents of oo2 as a list
     oo2            # oo2 itself
     oo2$parent.env() # same
     oo2$parent.env()$as.list() # contents of parent of oo2
     oo2$print()
     oo2$ls()
     oo2$str()
     oo3 <- oo2
     oo2$identical(oo3)
     oo2$identical(oo)

     # start off with Root to avoid problem cited in Note
     Root <- proto()
     oop <- Root$proto(a = 1, incr = function(.) .$a <- .$a+1)
     ooc <- oop$proto(a = 3) # ooc is child of oop but with a=3
     ooc$incr()
     ooc$a      # 4

     # same but proto overridden to force a to be specified
     oop$proto <- function(., a) { .super$proto(., a=a) }
     ## Not run: 
     ooc2 <- oop$proto() # Error. Argument "a" is missing, with no default.
     ## End(Not run)
     ooc2 <- oop$proto(a = 10) 
     ooc2$incr()
     ooc2$a # 11

     o2 <- proto(a = 1, incr = function(.) .$a <- .$a+1)
     o2c <- as.proto(o2$as.list()) # o2c is a clone of o2
     o2d <- o2$proto()  # o2d is a delegate of o2
     o2$a <- 2
     o2c$a  # a not changed by assignment in line above
     o2d$a # a is changed since a not found in o2d so found in o2

