Back to library index.

Package std-oxy (in std.i) - object oriented extensions to yorick

Index of documented functions or symbols:


DOCUMENT f = closure(function, data)
      or f = closure(object, member)
  creates a closure function from FUNCTION and DATA.  Invoking the
  closure function invokes FUNCTION with argument DATA prepended
  to the argument list passed to the closure function.  For example,
     f;        is equivalent to    FUNCTION, DATA;
     f(a1,a2)  is equivalent to    FUNCTION(DATA, a1, a2)
  and so on.  When the first argument is an OBJECT, and the second
  argument a MEMBER, the object is invoked with the member as its
  first parameter.  (Typically the member would be a function.)
  If the first argument is an object, then the second argument
  (MEMBER) has the same semantics as object(member, ...): namely,
  if MEMBER is a simple variable reference, its value is ignored,
  and only its name is used to specify which member of the object.
  Hence, if you want the value of MEMBER to be used, you need to
  be sure the second argument to closure is an expression, such
  as noop(member).  If the first argument is a function, then
  the second argument is always a value.

  Finally, the first argument can be a string in order to specify
  that the returned closure object use the value of the named
  variable at runtime for the function (or object).  If you expect
  the value to be an object, prefix the variable name by "o:" in
  order to prevent the closure object from keeping a use of the
  value of the second argument, if MEMBER is a simple variable
  reference.  For example,
    f = closure("myfunc", data);     // keeps use of data value
    g = closure("o:myobj", member);  // ignores value of member
    h = closure("myobj", noop(member));  // o: unnecessary
  This feature is primarily an aid during debugging; typically
  you would remove the quotes (and o:) once the code was working.

  You can query a closure object using the member extraction
    f.function  returns function or object      returns data or member
    f.function_name  returns name of function or object
    f.data_name      returns name of data
  The names are string(0) if unknown; function_name is known
  if and only if the first argument to closure was a string;
  data_name is known only if the first argument was a string or
  an object and the second argument a simple variable reference.

SEE ALSO: oxy, is_func, use


DOCUMENT flags = gaccess(grp)
      or grp = gaccess(grp, flags)
  With single GRP argument, return current group object access flags.
  With second FLAGS argument, set group object access flags, returning
  the input GRP to allow constructs like g=gaccess(save(var1,var2), 3);
  The access flags bits are:
    1  set if no new members may be created
    2  set if existing members cannot change data type or dimensions
         (that is, they behave as x(..)=expr, rather than as x=expr)

SEE ALSO: oxy, save, restore


DOCUMENT is_obj(x)
      or is_obj(x,m)
      or is_obj(x,m,errflag)
  returns 1 if X is an object, else 0.  If X is an object which permits
  numerical indexing of its members, returns 3.  With second parameter M,
  query is for member M of object X.  If M specifies multiple members
  (an index range, index list, or list of member names), then returns
  an array of results.  Note that is_obj(x,) may return [] if the
  object X is empty.  With third parameter ERRFLAG non-nil and non-zero,
  is_obj will return -2 if X is not an object, and -1 wherever M
  specifies a non-member (either a name not present or an index out
  of range).  Without ERRFLAG, is_obj raises an error when M is not
  a member.

SEE ALSO: oxy, save, restore


  Object extension to yorick.  Various yorick packages may create
  "objects", which are collections of data and methods (functions)
  for operating on that data.  Yorick objects are much more free-form
  than other object-oriented languages, in keeping with the fact that
  yorick has no declarative statements.

  Yorick objects have zero or more members; members may be anonymous
  or named.  For objects supporting anonymous members (an optional
  feature), all members whether anonymous or named can be accessed
  by a 1-origin index, as if the members were a 1D array.  Object
  indexing has unusual semantics, similar to the semantics of the
  arguments to the save and restore commands, in which it is the
  name of the variable passed as an argument to the object, rather
  than the value of the variable, which determines which member is
  to be extracted.  For example,
  refers to the member of obj named "i", whether the value of i is
  1 or 100 or "j" or sqrt(3)+2i or span(0,1,200).  However, by passing
  an expression, rather than a simple variable reference, you can
  make the value of the argument (now that it has no name) be significant.
  Hence, obj("i") is also member "i", while obj(7) is the 7th member,
  obj(3*n+2) is the 3*n+2nd member, and so on.  You can use the noop()
  function to make a variable into an expression, if the member specifier
  happens to be stored in a variable:

  Objects also accept some special arguments:
    obj()   returns the whole object (same as without the parens)
    obj(*)  returns the number of members
    obj(*,) returns an array of member names (string(0) for anonymous)
              in the index order (if the object supports indexing)
    obj(*,m)  returns an array of the specified member names, or the
              specified member indices if M is a string array
              (if the object supports indexing)
    obj(..) returns the attribute object associated with obj, which
              may be an empty object, or nil [] if obj does not support

  When called as a subroutine, objects accept keyword arguments as a
  shorthand for the save command:
    obj, m1=val1, m2=val2, ...;
  is the same as:
    save, obj, m1=val1, m2=val2, ...;

  Yorick has a generic object, called a "group", which holds an arbitrary
  collection of yorick variables.  You make group objects with the save
  function (see help,save), and you can also use save to add members to
  an existing group object.  Another way to create a group object is by
  passing a membspec argument to any group which specifies multiple
  members -- the result will be a group containing all the specified
  members.  Hence, membspec may be an array of strings, an array of
  indices, or an index range min:max:step, in order to produce a group
  object holding all the specified members.  (Note that dimensionality
  of membspec arrays is lost, and potentially not even number is
  preserved if a single named member is specified multiple times.)

  When you pass more than one argument to an object, the first one
  specifies a single member, and subsequent arguments apply to that
  member.  Thus,
    obj(m, i, j, k)    is similar to     obj(m)(i, j, k)
  In fact, these are exactly the same as long as M does not specify
  a function.  When M is a function, there is a slight difference,
  which is that the function (whether built-in or interpreted) is
  executed in the context of the object obj.  Unlike classic object
  oriented languages, in yorick the use of the context object is not
  automatic -- the function M must specify which object members it
  wishes to access.  You do that with the use function:
func method
      extern var1, var2, var3;
      use, var1, var2, var3;   // initializes vari from context object
      // compute using var1, var2, var3, possibly redefining them
      return result;
      // just before return, any
      // changes to var1, var2, var3 stored back to context object
  In this form, the arguments to the use call must be external variables
  to the function (method).  If you put the use call(s) at the top of the
  function, you can dispense with the explicit extern statement, provided
  you are careful to check that none of the names matches any of the
  dummy parameter names (x, y, or z here).  [Eventually, the yorick
  parser may treat use specially and enforce this restriction.  For now,
  you need to be sure that any arguments are external to avoid incorrect
  behavior.  Specifically, if any of the vari is local, the external
  variable of that name will be set to nil (or the actual argument value
  if vari is a dummy parameter) when the method returns.  Ouch.]
  Although the vari look like they are extern to the method function,
  use saves their external values and arranges to replace them when the
  function returns, so they behave as if they were local to method.

  You want to restrict the "use" subroutine/declarative to only those
  object members which the method function changes.  If you merely wish
  read access, you have two options, either call a special form of
  restore, or call use() as a function in expressions.  For example,
  suppose you are going to modify var1 and var3, but merely read var2
  and var4 in the method context:
func method
      use, var1, var3;   // initializes vari from context object
      local var2, var4;  // otherwise, restore arguments would be extern
      restore, use, var2, var4;
      // compute using vari
      var1 = something(var2);   var3 = something(var4);
      return result;
  Or equivalently,
func method
      use, var1, var3;   // initializes vari from context object
      // compute using vari
      var1 = something(use(var2));   var3 = something(use(var4));
      return result;
  As a function, use(arg1, arg2, ...) is exactly the same as
  obj(arg1, arg2, ...), where obj is the context object for the function.

  You can a invoke method function M as a subroutine as well:
    obj, m, i, j, k;

  This stripped down facility lets you do most of the things (except
  arguably type checking) other object oriented languages feature,
  although it is a little difficult to see how to do this at first.
  For example, you can think of a "class" as the constructor function
  which makes instances:
func myclass
      // build and return the class, for example:
      return save(method1, method2, ..., data1, data2, ...);
func method1
func method2
  You make an instance with:
    mything = myclass(d1, d2);
  Then you can use your object: mything(method2,x,y) and so on.

  This is slightly untidy, because you have to worry about never
  colliding with the method names.  So you could bundle it up neatly
  by making myclass itself an object containing all its methods and
    myclass = save(new, method1, method2, ...);  // save old values
func new
      // build and return the class, for example:
      return save(method1, method2, ..., data1, data2, ...);
func method1
func method2
    myclass = restore(myclass);   // swap new values into myclass
  (See help,save for examples of this trick.)  Now the only variable
  you have to worry about not clobbering is myclass, and you create
  your object with:
    mything = myclass(new, d1, d2);
  and use it as before.

  There are many ways to handle inheritance (multiple inheritance
  is no harder); the simplest is just to use the save function to
  concatentate new members onto the base class.  [Probably should
  illustrate a good style here...]  The complete absence of type
  checking is actually an advantage here: If an object has a method
  with the required arguments and semantics, it will be usable no
  matter what other members it has.

  You can also extract object members using the dot operator:
    obj.member    is a synonym for    obj("member")
  but note that obj.member(args) may be very different from
  obj(member,args); in general you are better off not using the
  dot operator with objects.  In particular, note that
    obj.member = value;   // ERROR!
  does NOT set the member to value.  (Use obj,member=value;)

SEE ALSO: use, save, restore, is_obj, openb, createb, noop, closure, gaccess


DOCUMENT use, var1, var2, ...
      or use(membspec, arg1, arg2, ...)
  Access the context object in an object method function (see help,oxy).

  In the first form, the VARi must be extern to the calling function,
  and you get read-write access to the VARi.  That is, if you redefine
  any of the VARi, your changes will be saved back to the context object
  when the calling function returns.  Even though the VARi are external
  to the method function, use arranges for their external values to be
  replaced when the calling function returns, just as if they had been
  local variables.

  The second form is equivalent to obj(membspec, arg1, arg2, ...),
  where obj is the context object.  You can use this whenever you need
  only read access to membspec.

  Alternatively, you can use the special forms of the save and
  restore function to explicitly save and restore variables from the
  context object:
    restore, use, var1, var2, ...;
    save, use, var1, var2, ...;
  The use function is merely a shorthand for these explicit operations,
  so you do not need to worry about multiple return points in the
  method function or other details.

SEE ALSO: oxy, save, restore, openb, createb, noop, closure