home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / modula3 / 908 < prev    next >
Encoding:
Internet Message Format  |  1992-09-13  |  5.3 KB

  1. Path: sparky!uunet!sun-barr!olivea!mintaka.lcs.mit.edu!nntp.lcs.mit.edu!andru
  2. From: andru@concerto.lcs.mit.edu (Andrew Myers)
  3. Newsgroups: comp.lang.modula3
  4. Subject: Constructors in Modula-3
  5. Message-ID: <ANDRU.92Sep14011727@concerto.lcs.mit.edu>
  6. Date: 14 Sep 92 06:17:27 GMT
  7. Sender: news@mintaka.lcs.mit.edu
  8. Distribution: comp
  9. Organization: MIT Laboratory for Computer Science
  10. Lines: 133
  11.  
  12.  
  13. I am very fond of Modula-3, but it has one lack that periodically
  14. annoys me: it doesn't have constructors. A constructor, in C++
  15. parlance, is a special initializer associated with a class. Objects of
  16. that class cannot be created except by using one of the constructors of
  17. the class. This restriction allows a class designer to ensure that
  18. objects of the class will always be properly initialized.
  19.  
  20. By contrast, if I see the Modula-3 declaration
  21.  
  22.     TYPE T <: U;
  23.  
  24. I can write the statement
  25.  
  26.     VAR x := NEW(T);
  27.  
  28. and obtain an object that has not been initialized. Maybe it's a valid
  29. object of that type; maybe not.
  30.  
  31. Modula-3 programmers, in my experience, handle this problem through the
  32. use of conventions and careful documentation. Any interface that
  33. describes a Modula-3 type should clearly state what is needed to
  34. obtain a valid object of the type.
  35.  
  36. The most obvious and common convention is a "New" procedure. To create
  37. an object of type Interface$T, we say
  38.  
  39.     VAR x := Interface$New(...)
  40.  
  41. where the "..." represents the arguments that are needed to initialize
  42. an object. The approach works until you try to subclass, since the
  43. "New" procedure of type "S" with S<:T cannot use T's New to initialize
  44. the T part of S.
  45.  
  46. Another standard convention I've seen used is an "init" method. This
  47. method is used in the following way:
  48.  
  49.     VAR x := NEW(T).init(...)
  50.  
  51. The "init" method is seldom overridden, and doing so is never useful.
  52. Consider that "NEW(T).init(...)" is identical to "T.init(NEW(T), ...)"
  53. if T reveals that it overrides the "init" method.  The fact that "init"
  54. is made a method is a matter of convenient notation.  Though less
  55. efficient, "NEW(T).init()" is easier to type and read than
  56. "Interface_T$Init(NEW(T))".
  57.  
  58. Both of these variants avoid the limitations of "New", since the
  59. subtype's "Init" procedure can be written as
  60.  
  61. PROCEDURE Init(x: S, S_args...) =
  62. BEGIN
  63.     Interface_T$Init(x, T_args...) (* or Interface_T.T.init(x, T_args...) *)
  64.     (* Rest of S initialization here *)
  65. END Init;
  66.  
  67. All of these solutions require careful programming, since an
  68. initialization method must be sure to properly initialize all of the
  69. fields of an object, and to properly initialize the supertype. Since
  70. Modula-3 guarantees that each field of an object is initialized by the
  71. NEW operator to some value of its declared type, all of these solutions
  72. also overwrite existing values in the fields. This reinitialization is
  73. only a potential inefficiency, since any old garbage is considered to
  74. be a valid value of any type. If future implementations choose a more
  75. intermediate point on the debuggability/performance curve, the
  76. inefficiency will become real.
  77.  
  78. More importantly, ensuring that variables and values are properly
  79. initialized is a major source of programming errors, and it is
  80. unfortunate that Modula-3 doesn't help the programmer out more.
  81.  
  82. Here is a proposal for constructors in Modula-3, just as food for
  83. thought:
  84.  
  85. In any module that fully reveals (i.e., contains "REVEAL T = ... ") an
  86. object type T, a "constructor" may be declared. A constructor looks
  87. much like a procedure, but it may only be used in the place of the NEW
  88. operator, or within another constructor's body . Thus, to create a new
  89. object of the type T with a constructor named "Make", we could say
  90.  
  91.     VAR x := Make(...)
  92.  
  93. Let T have a supertype named U with a constructor named Make_U and
  94. a method named "index", fields named "a" and "b" of types "A" and "B",
  95. and a field "i" of type "INTEGER". The constructor implementation syntax
  96. looks like:
  97.  
  98. CONSTRUCTOR T Make(a_: A) =
  99. VAR tmp := Make_B();
  100.     (
  101.     Make_U(a_, ....),
  102.     a := a_,
  103.     b := tmp,
  104.     i := tmp.index()
  105.     )
  106.  
  107. A constructor body contains a list of comma-separated items. At most
  108. one item may be a constructor invocation for some supertype of T.  Call
  109. this supertype "U".  All of the subtypes of U must be fully revealed in
  110. the scope of the constructor, implying that a supertype constructor
  111. must be used on a subtype of T's closest opaque supertype, if any.
  112.  
  113. Each of the remaining items is an assignment to a field belonging to T,
  114. or to a local variable of the constructor.  Every field of T that is
  115. not contained in U and does not have a default value must be assigned
  116. to within the constructor body.
  117.  
  118. Because the object being constructed cannot be named, this
  119. approach avoids the problem of defining the semantics of method
  120. invocations on a partially constructed object, and the consequent
  121. inefficiency that C++ creates during object construction.
  122.  
  123. A constructor can be declared in an interface, and the syntax is
  124. similar to procedure-declaration syntax, e.g.:
  125.  
  126. CONSTRUCTOR T Make(a_: A);
  127.  
  128. A call to NEW is just like a constructor body: in addition to the usual
  129. override assignments, one of the arguments to NEW(T, ...) may be a
  130. constructor invocation. NEW may only be called on an opaque type if a
  131. constructor is used. A call to NEW is required to initialize all of T,
  132. just as a constructor body is.
  133.  
  134. Thus, 
  135.  
  136.     x := Make(...)
  137.  
  138. is really just shorthand for
  139.  
  140.     x := NEW(T, Make(...))
  141.  
  142. if "Make" is a constructor for "T".
  143.  
  144. Andrew
  145.