You can subscribe to this list here.
2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(83) |
Aug
(46) |
Sep
(87) |
Oct
(77) |
Nov
(83) |
Dec
(53) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2010 |
Jan
(121) |
Feb
(15) |
Mar
(133) |
Apr
(125) |
May
(104) |
Jun
(20) |
Jul
(21) |
Aug
(8) |
Sep
(46) |
Oct
(51) |
Nov
(37) |
Dec
(21) |
2011 |
Jan
(28) |
Feb
(12) |
Mar
(36) |
Apr
(18) |
May
(10) |
Jun
(18) |
Jul
(32) |
Aug
(16) |
Sep
(12) |
Oct
|
Nov
|
Dec
(4) |
2012 |
Jan
(1) |
Feb
(5) |
Mar
(1) |
Apr
(4) |
May
(3) |
Jun
(3) |
Jul
(19) |
Aug
(5) |
Sep
(1) |
Oct
(7) |
Nov
|
Dec
(4) |
2013 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
(2) |
Jun
(3) |
Jul
(1) |
Aug
(1) |
Sep
|
Oct
|
Nov
|
Dec
|
2014 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(7) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2015 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
(5) |
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
|
2017 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2019 |
Jan
(6) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Vijay S. <vi...@sa...> - 2009-07-08 05:49:01
|
Summarizing for Nate and Dave C and to get it on the record. Dave G and Igor are unhappy with a design in which structs can have virtual inheritance and are required to not have a vtable-based implementation. Their point is that this would cause considerable code bloat --- particularly in a realistic deep class hierarchy designed for reuse. They fear the kind of automatic code generation design that Self had -- worked great for small programs, could not handle 10KLOC programs. They understand that the virtue of having structs implement interfaces and have a subtyping relation is that it is easier to write generic code. One can supply uniform constraints on type parameters (such as X behaves as I, for an interface I) and have the type parameter be instantiated with a struct or a class. So they are proposing that structs be limited to not have virutal inheritance but are limited to C++ struct style non-virtual inheritance, which does not require a vtable. There are two approximations of this within X10: require all methods on structs to be declared as final, or require all methods to be static. structs may still implement interfaces. We are tentatively going with the proposal that all methods on structs are automatically final. (Cannot be overridden or shadowed.) Need to work on some code to see whether this is reasonable. So the idea is that we permit the user to directly define structs or classes. Structs may extend other structs and implement interfaces, but may only have final methods. Corresponding to every struct S is an automatically generated normal class also named S. For a value s of type S we can use the new operator to get a value new s of type class S. I will rework the 1.5 design on the wiki to include this change. Another issue that came up was the type hierarchy, and the fact that while Any may be implementable (in the presence of arbitrary user-defined structs) it is pretty pointless (i.e. costly) having to upcast a value to Any in order to perform a .equals(...) operation on it. So we need to find a way to define .equals (i.e. specify its type) without forcing Any to exist. This was addressed in my previous message. |
From: Vijay S. <vi...@sa...> - 2009-07-08 05:29:02
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> </head> <body bgcolor="#ffffff" text="#000000"> Problem: Given a two-headed type system -- with <tt>struct Struct</tt> and <tt>class Object</tt> as the two heads -- how does one provide a signature for the <tt>equals</tt> method?<br> <br> Solution: <br> <br> (0) Distinguish between two relationships on types: <br> <ul> <li> <tt>_ is a subtype of _</tt> (written: <tt>_ <: _</tt>). If <tt>S <: T</tt>, then in every context a value of type <tt>S</tt> can substitute for a value of type <tt>T</tt>. Thus a value of type <tt>S</tt> can be assigned to a variable of type <tt>T</tt>. <br> </li> <li> <tt>_ behaves as _</tt> (written: <tt>_ <| _</tt>). If <tt>S</tt> behaves as <tt>T</tt> then all methods available on <tt>T</tt> are available on <tt>S</tt>, but it is not necessarily the case that a value of type s can be assigned to a variable of type T. For instance, S may be a struct and I an interface.</li> <li><tt>S <: T</tt> implies <tt>S <| T</tt><br> </li> </ul> (1) Introduce <tt>x.Type</tt>. For any struct or object x, <tt>x.Type</tt> is the struct or class of which x is an instance. <br> <br> (2) Assert (and ensure) that the X10 type system validates: <tt><br> </tt> <blockquote><tt>for all x. (x.Type <: Struct or x.Type <: Object)</tt><br> </blockquote> <br> (3) Define <br> <br> <tt>interface Equals {<br> def equals[T](x:T){this.Type <: T}:boolean;<br> }</tt><br> <br> (4) Have Struct implement Equals:<br> <br> <tt>package x10.lang;<br> abstract struct Struct implements Equals {}</tt><br> <br> Hence <tt>Struct <| Equals</tt><br> <br> (5) Have Object implement Equals:<br> <br> <tt>package x10.lang;<br> class Object implements Equals {<br> def equals[T](x:T){this.Type <: T} = this == x;<br> ...<br> }<br> </tt>Hence <tt>Object <| Equals<br> <br> </tt>(5) Hence the type system can establish: <tt>for all x. x.Type <| Equals. </tt>Therefore in the body of a class G with a type parameter X, and x:X, it is legal to invoke <tt>x.equals(y)</tt> as long as it can be established that <tt>y:Y</tt> and <tt>X <: Y</tt>.<br> </body> </html> |
From: David P G. <gr...@us...> - 2009-07-07 18:32:25
|
David Cunningham/Watson/IBM wrote on 07/06/2009 08:45:28 PM: > > I think there is only one thing that has been proposed, that is not > supported directly by C++ in some way. This is why most of what has > been proposed has compiled down to C++ very easily. > > The one thing I can think of: We want to support the omission of > vtable pointers from inlined instances of classes. C++ doesn't need > this because in C++ the programmer can define classes that do not > have any virtual functions, thus have no vtables. In X10 this would > not be possible since every class has at least the virtual functions > inheritted from Object. You could argue C++ has something like the > primitives proposal, but it is a lot more powerful since its > primitives can have mutable fields, and support inheritance. > > If we are *not* getting rid of the vtable pointer from inlined > instances of classes, and inlineable classes must be immutable (this > I believe is the proposal for November), then there is nothing in > that proposal that is not supported directly by C++. C++ is more > powerful, however, since it supports inlining of mutable classes. > I think this is the key issue. What we can reasonably expect to do is to expose to the X10 programmer some safe subset of what C++ already implements for us intersected with the additional constraints of our distributed object model. The current design strays outside of that space, and that's where I think it gets us into trouble. --dave |
From: Vijay S. <vi...@sa...> - 2009-07-07 05:37:35
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> </head> <body bgcolor="#ffffff" text="#000000"> You can now start from <a class="moz-txt-link-freetext" href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign">http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign</a> on the internal wiki.<br> <br> Nate, here are most of the relevant links.<br> <br> Best,<br> Vijay<br> <br> <ul> <li> Initially created: 14 June 2009. </li> <li> Last updated: vj 6 July 2009. </li> <li> Base link: <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign" class="twikiLink">XtenTwoOhDesign</a> </li> </ul> <p></p> <h1><a name="Design_of_X10_2_0_Version_1_5"></a><a name="Design_of_X10_2_0_Version_1_5_"></a> Design of X10 2.0 (Version 1.5) </h1> <p> This page contains links to a set of wiki pages documenting the design of X10 2.0. Ultimately these pages will be incorporated into a standalone design document. Text based on these pages will be included in the Language Reference. </p> <p></p> <p> </p> <ul> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhOverview" class="twikiLink">XtenTwoOhOverview</a> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhObjectModel" class="twikiLink">XtenTwoOhObjectModel</a> </li> <li> <strike>XtenTwoOhPrimitives</strike> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhSimpleStructs" class="twikiLink">XtenTwoOhSimpleStructs</a> </li> <li> <strike>XtenTwoOhTypeSystem</strike> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhStatics" class="twikiLink">XtenTwoOhStatics</a> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhXtenLangObject" class="twikiLink">XtenTwoOhXtenLangObject</a> </li> <li> <strike>XtenTwoOhXtenLangNullable</strike> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhXtenLangRail" class="twikiLink">XtenTwoOhXtenLangRail</a> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhXtenLangValRail" class="twikiLink">XtenTwoOhXtenLangValRail</a> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhXtenLangPoint" class="twikiLink">XtenTwoOhXtenLangPoint</a> </li> <li> <span class="twikiNewLink">XtenTwoOhXtenLangRegion<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/XtenTwoOhXtenLangRegion?topicparent=Main.XtenTwoOhDesign" title="Create this topic"><sup>?</sup></a></span> </li> <li> <span class="twikiNewLink">XtenTwoOhXtenLangDist<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/XtenTwoOhXtenLangDist?topicparent=Main.XtenTwoOhDesign" title="Create this topic"><sup>?</sup></a></span> </li> <li> <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhNativeDesign" class="twikiLink">XtenTwoOhNativeDesign</a></li> <li><br> </li> </ul> <br> <ul> <li> Initially created: 14 June 2009 </li> <li> Last updated: vj 6 July 2009. </li> <li> Base link: <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign" class="twikiLink">XtenTwoOhDesign</a> </li> </ul> <p></p> <h1><a name="The_X10_2_0_Object_Model_Overvie"></a> The X10 2.0 Object Model Overview (Version 1.5) </h1> <p> <strong>X10 is a class-based, object-oriented, generic, global programming language, supporting user-definable primitives, closures and a dependent type system.</strong> </p> <p></p> <h2><a name="Class_based"></a> Class-based </h2> <p> As in Java-like languages, an X10 program consists primarily of a collection of <em>classes</em> and <em>interfaces</em>. Classes are organized in a single-inheritance hierarchy, with root <code>x10.lang.Object</code>. </p> <p>A class specifies a collection of <em>properties</em>, (mutable and immutable) instance and static <em>fields</em>, <em>constructors</em>, and (overloaded) <em>methods</em>. A property is a <code>final</code> instance field that can be used in constructing types based on constraints. Properties are required </p> <p>A class may implement multiple <em>interfaces</em>. An interface specifies a collection of methods and properties that must be implemented by a class. </p> <p></p> <h2><a name="Generic"></a> Generic </h2> <p> X10 classes and methods may take <em>generic type parameters</em>. The parameters may be constrained by clauses which specify bounds on parameters (e.g. <code>X <: Comparable[X,X]</code>), and hence specify properties and methods available at that type. </p> <p>If <code>T1,..., Tn</code> is a sequence of types satisfying the constraint <code>c</code> associated with a class <code>C[X1,..., Xn]</code> then <code>C[T1,..., Tn]</code> is a legal instantiated class. The code for this class is obtained from the code for <code>C[X1,..., Xn]</code> by replacing each <code>Xi</code> with <code>Ti</code> (heterogeneous translation). </p> <ul> <li> This is how the <em>semantics</em> of generic classes is defined. The implementation is free to choose a different strategy as long as it is faithful to the semantics. For instance it may choose to make a heterogeneous translation only if an actual type parameter is a <code>struct</code> type. </li> </ul> <p></p> <h2><a name="Global_state"></a> Global state </h2> See <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhObjectModel" class="twikiLink">XtenTwoOhObjectModel</a> for details. <p>Place <code>0</code> contains the static state for all classes. </p> <p>Instances of a class are created by invoking a constructor using <code>new</code>. Variables may store <em>references</em> to objects. Each object has a globally unique identity, two object references are equal (<code>=&#61</code>) only if they point to the same object. </p> <p>The instance state of a class is divided into <em>local</em> and <em>global</em> state. All <code>var</code> instance fields of a class are local; some <code>val</code> instance fields may be marked <code>local</code> by the programmer. The <code>local</code> state of an object is accessible only in the place in which the object is created; this location is called the <em>home</em> of the object and is available through the <code>home</code> property of the object. The global state of an object can be accessed from any place. </p> <p>Methods are also classified as <code>local</code> or <code>global</code>. The compiler ensures that an object's <code>local</code> methods can only be called from the <code>home</code> of the object. </p> <p></p> <h2><a name="User_defined_primitives"></a> User-defined primitives </h2> <p> See <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhSimpleStructs" class="twikiLink">XtenTwoOhSimpleStructs</a> for details. </p> <p>An instance of a class <code>C</code> (an <em>object</em>) is represented in X10 as a contiguously allocated chunk of words in the heap, containing the fields of the object as well as one or more words used in method lookup. Variables with base type <code>C</code> (or a supertype of <code>C</code>) are implemented as cells with enough memory to hold a <em>reference</em> to the object. The size of a reference (32 bits or 64 bits) depends on the underlying operating system. </p> <p>For many high-performance programming idioms, the overhead of one extra level of indirection represented by an object is not acceptable. For instance, a programmer may wish to define a type <code>complex</code> (consisting of two <code>double</code> fields) and require that instances of this type be represented precisely as these two fields. A variable or field of type <code>complex</code> should, therefore, contain enough space to store two <code>doubles</code>. An array of <code>complex</code> of size <code>N</code> should store <code>2*N</code> doubles. Parameters of type <code>complex</code> should be passed inline to a method as two doubles. If a method's return type is <code>complex</code> the method should return two doubles on the stack. </p> <p>X10 supports user-defined primitives (called <em>structs</em>). </p> <ul> <li> For C/C++ programmers, an X10 struct corresponds to a C/C++ struct all of whose fields are <code>const</code>. Such structs do not have any aliasing issues. </li> </ul> <p>For every class <code>C</code> all of whose fields are <code>val</code> fields, X10 automatically defines a type <code>struct C</code> with the same fields as <code>C</code>. </p> <ul> <li> Thus <code>struct Object</code> is the root of the <code>struct</code> hierarchy. </li> </ul> <p>Unlike objects, structs do not have global identity. Instead, two structs are equal <code>==</code> if and only if their corresponding fields are equal <code>==</code> (this is the central property of structs). This implies that a variable of type <code>struct C</code> can contain only values of type <code>struct C</code> -- and not values of type <code>struct D</code> (for some subclass <code>D</code> of <code>C</code>). We say that _struct types are exact types_ to mean that a variable declared at a struct type must take on precisely the values of that type (and not a subtype). </p> <p> </p> <ul> <li> A value <code>v</code> of type <code>struct D</code> can be converted to a value of type <code>struct C</code> using the <code>v to struct C</code> conversion expression, if <code>D</code> is a subclass of <code>C</code>. Such a conversion retains only those fields of <code>D</code> which are fields of <code>C</code>. </li> </ul> <p>The size of a variable of type <code>struct C</code> is the size of the fields defined at <code>C</code>. </p> <p>X10 permits an expression of type <code>C</code> to be assigned to a variable of type <code>struct C</code> (this results in copying the fields of the object on the RHS to the fields of the variable on the LHS). The expression =new v= can be used to get a value of type <code>C</code> from a value <code>v</code> of type <code>struct C</code>, initialized with the state of <code>v</code>. </p> <p> </p> <ul> <li> To better support generic programming, <code>struct v</code> is permitted for <code>v</code> of type <code>struct C</code> as well. It simply copies the value of <code>v</code>. </li> </ul> <p> </p> <ul> <li> Similarly, <code>new v</code> is permitted for <code>v</code> of type <code>C</code> as well. It returns a <em>clone</em> initialized with the state of <code>v</code>, and guaranteed to be <code>!=</code> from <code>v</code> only if <code>v</code> has mutable state. </li> </ul> <p>The methods available on <code>struct C</code> are all those methods not marked <code>ref</code> on <code>C</code>. X10 requires that a method <code>m</code> for class <code>C</code> must be marked <code>ref</code> if it uses <code>this</code> in any context that distinguishes between the types <code>C</code> and <code>struct C</code>. Specifically <code>this</code> may not be an argument of cast, <code>==</code>, <code>!&#61</code> or <code>instanceof</code> operators, and may not be passed into a method at type <code>C</code> (or a supertype) or returned from a method with return type <code>C</code> (or a supertype) or assigned to a variable of type <code>C</code> (or a supertype). </p> <p> </p> <ul> <li> In all these cases the programmer may use <code>new this</code> instead. </li> <li> A method marked <code>ref</code> can only be overridden by a method marked <code>ref</code>. </li> </ul> <p>In methods not marked <code>ref</code>, <code>this</code> ambiguously has the type <code>struct C</code> or <code>C</code>. </p> <p>The subtype relationship on structs is obtained from the subtype relationship of the underlying classes. If <code>C</code> is a subclass of <code>B</code>, then <code>struct C</code> is a subtype of <code>struct B</code>. If <code>C</code> implements the interface <code>I</code>, then <code>struct C</code> implements the interface =struct I=. </p> <ul> <li> Variables cannot be declared at the type <code>struct I</code>, for <code>I</code> an interface. Such types are of use in placing bounds on type variables. </li> </ul> <p>Since <code>struct</code> types are exact types, the primary use of the subtype relationship on <code>structs</code> is in imposing constraints on type parameters (e.g. <code>X <: struct C</code>). The constraint <code>X <: Object</code> can be used to ensure that <code>X</code> can only be instantiated with classes. Similarly, <code>X <: struct Object</code> ensures that <code>X</code> can only be instantiated with structs. </p> <p></p> <h2><a name="Closures"></a> Closures </h2> <p> See <span class="twikiNewLink">XtenTwoOhClosures<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/XtenTwoOhClosures?topicparent=Main.XtenTwoOhOverview" title="Create this topic"><sup>?</sup></a></span> for details. </p> <p>X10 supports closures and closure literals. For any expression <code>e</code> of type <code>T</code> with free variables <code>x1,..., xn</code> of type <code>T1,..., Tn</code> respectively, the expression <code>(x1:T1,..., xn:Tn):T =>e</code> is a value of type <code>(T1,..., Tn)=>T</code>. If <code>f</code> is a value of such a type then <code>f(e1,..., en)</code> is of type <code>T</code> provided that each <code>ei</code> is of type <code>Ti</code>. </p> <p>The qualifier <code>local</code> may be used on type <code>(T1,..., Tn)=>T</code> to indicate that that values of this type must be applied only in the place in which this value was created. </p> <p></p> <p></p> <h2><a name="Dependent_type_system"></a> Dependent type system </h2> <p> For <code>C</code> a class and <code>c</code> a <em>constraint expression</em> then <code>C{c}</code> is a type. <code>c</code> may reference <code>val</code> variables currently in scope, and the special variable <code>self</code> which may be used to access the properties of <code>C</code>. An object <code>o</code> is of type <code>C{c}</code> if the constraint expression evaluates to true when executed in an environment in which the the <code>val</code> variables in scope are assigned any type-correct values, and <code>self</code> is assigned <code>o</code>. </p> <p> </p> <ul> <li> Thus, the type <code>Region{self.rank==2}</code> is satisfied by all <code>Region</code> objects whose <code>rank</code> property contains the value <code>2</code>. </li> </ul> <p>We define in a similar fashion what it means for a struct <code>o</code> to be of type <code>struct C{c}</code>. </p> <p></p> <p></p> <h3><a name="Type_definitions"></a> Type definitions </h3> <p> X10 also permits the programmer to specify <em>type definitions</em>. A type-definition specifies a name (e.g. <code>n</code>), an optional list of type parameters (e.g. <code>X1,...,Xn</code>) and an optional list of value parameters (e.g. <code>y1,..., yk</code>), and a type <code>T</code> which can refer to these parameters. An invocation <code>n[T1,.., Tn](e1,..., ek)</code> then stands for the type <code>T</code> in which the actuals have been substituted for the formals. Thus for instance </p> <p></p> <pre>typdef Pair[X,Y](x:X,y:Y)= Pair[X,Y]{self.x==x, self.y==y} </pre> permits us to use <code>Pair[int,int](0,1)</code> as a type whose only member is the <code>Pair</code> whose first component equals <code>0</code> and the second component equals <code>1</code>. <p>Type definitions may be top-level members of packages. </p> <p>A <code>public typedef</code> with name <code>n</code> must be specified in the file <code>n.x10</code> to facilitate separate compilation. </p> <p>Type definitions are expanded at compilation time. Recursive type definitions are not permitted; the compiler may not terminate when processing such type definitions. </p> <p></p> <p></p> <h2><a name="Class_hierarchy"></a> Class hierarchy </h2> <p> The package <code>x10.lang</code> defines the following classes </p> <p></p> <pre>Object Rail[X] // classes with immutable state ValRail[X] Point Region Dist Place String // immutable state Boolean // typedef boolean = struct Boolean; Byte // typedef byte = struct Byte; Short // typedef short = struct Short; Integer // typedef int = struct Integer; Long // typedef long = struct Long; Float // typedef float = struct Float; Double // typedef double = struct Double; UByte // typedef ubyte = struct UByte; UShort // typedef ushort = struct UShort; UInteger // typedef uint = struct UInteger; ULong // typedef ulong = struct Ulong; Char // typedef char = struct Char; Exception NullPointerException BadPlaceException ArrayIndexOutOfBoundsException ClassCastException ClockUseException IllegalOperationException RankMismatchException Error OutOfMemoryError Runtime System </pre> <p> </p> <h1><a name="TODO"></a> TODO </h1> <p> </p> <h1><a name="FAQ"></a> FAQ </h1> <p> </p> <h1><a name="Comments"></a> Comments </h1> <p> </p> <h1><a name="History"></a> History </h1> <p> </p> <p></p> -- <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/VijaySaraswat" class="twikiLink">VijaySaraswat</a> - 06 Jul 2009<br> <br> <ul> <li> Initially created: 14 June 2009 </li> <li> Last updated: vj 6 July 2009. </li> <li> Base link: <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign" class="twikiLink">XtenTwoOhDesign</a> </li> </ul> <p></p> <h1><a name="The_X10_2_0_Object_Model_Version"></a> The X10 2.0 Object Model (Version 1.5) </h1> <p> X10 2.0 has a rather simple <em>distributed object model</em>. </p> <p>The state of an object is partitioned into <em>global</em> state (a programmer defined subset of <code>val</code> fields) and <em>local</em> state. </p> <p> </p> <ul> <li> Field definitions are marked with the qualifier <code>local</code> if they are intended to be included in the local state. If the <code>local</code> qualifier is omitted, the field is considered global. Properties may not be marked <code>local</code>. <code>var</code> fields are implicitly marked <code>local</code>. <code>local</code> fields may be overridden only by <code>local</code> fields. </li> </ul> <p>Similarly, the methods of an object may be qualified as <code>local</code>; if they are not <code>local</code> they are considered global. Global methods may access only the global fields of an object. They must be written in such a way that they behave as intended when invoked from any place. </p> <ul> <li> A global method can always access <code>local</code> state or invoke a <code>local</code> method through an <code>at</code> statement, e.g. <code>at (this.loc()) { m() }</code>. </li> </ul> <p>Consider the execution of an <code>at (P) S</code> statement at a place <code>Q</code> different from <code>P</code>. Suppose <code>x</code> is an in-scope final local variable and contains a reference to an object <code>o</code> created at <code>Q</code>. Then within <code>S</code>, <code>x</code> is said to be a <em>remote reference</em> to <code>o</code> (references to <code>o</code> from place <code>Q</code> are said to be <em>local references</em>). X10 permits <code>global</code> fields to be read and <code>global</code> methods to be invoked through a remote reference. </p> <ul> <li> Remote references to an object <code>o</code> are implemented by serializing the global state of <code>o</code> across the network, together with information about the source place and the local reference to <code>o</code>. The data is deserialized at the receiver to create an implementation-level entity that is the remote reference. There is no requirement that the implementation intern such entities; however the implementation must correctly implement equality (see below). </li> </ul> <p>Like local references, remote references are first-class entities: they may be passed as arguments to methods, returned from methods, stored in fields of objects. </p> <p>Remote references may also be compared for equality (<code>==</code>). Two remote reference are equal if they are references to the same object. Equality is guaranteed to be a constant-time operation and not involve any communication. </p> <p>When a remote reference to an object <code>o</code> located at place <code>P</code> is transmitted to <code>P</code> it automatically becomes a local reference to <code>o</code>. Therefore the situation in which a local reference can be compared to a remote reference simply cannot arise. </p> <p>The class <code>x10.lang.Object</code> defines the global method <code>loc():Place</code>. When invoked on a (reference to) a rooted object <code>x</code> created at place <code>P</code>, <code>x.loc()</code> returns <code>P</code>. If <code>x</code> is a reference to a mobile object, <code>x.loc()</code> returns <code>here</code>. </p> <p>The X10 compiler ensures that <code>local</code> methods on <code>o</code> can only be invoked in a place where <code>here== o.loc()</code>, i.e. the place where <code>o</code> was created. (If <code>here== o.loc()</code>, we say <code>o</code> is <em>local</em>.) The programmer may always invoke a <code>local</code> method <code>m</code> on such an object through the code <code>at (o.loc()) { o.m() }</code>. </p> <p></p> <h3><a name="Local_execution"></a> Local execution </h3> <p> The semantics of <code>atomic</code> and <code>when</code> constructs requires that their bodies do not execute any <code>at</code> operations, implicitly or explicitly. Hence the compiler must establish that if a <code>local</code> method <code>m</code> is being invoked on a reference <code>o</code> in the body of such a construct, <code>o</code> is a local reference. </p> <p>To support compile-time analysis we introduce the type qualifier <code>local</code>. For any type <code>T</code>, a variable <code>x</code> of type <code>local T</code> can only contain local references, i.e. references to objects created in the "current" place. If <code>x</code> is a local variable, or a method parameter then the current place is <code>here</code>, the place at which the current activity is executing. If <code>x</code> is a field of an object <code>o</code>, it must be a <code>local</code> field, and must contain references to objects created at the same place as <code>o</code>. If the return type of a method is <code>local T</code> then it must return an object of type <code>T</code> created <code>here</code>. </p> <p> </p> <ul> <li> Thus it is legal (sound) for an activity executing the code of a <code>local</code> method for an object <code>o</code> to read the value of a <code>local</code> field of <code>o</code> of type <code>local T</code> into a local variable of type <code>local T</code>. </li> </ul> <p></p> <h3><a name="Object_hierarchy"></a> Object hierarchy </h3> <p> </p> <pre>class Object { def toString():String; def hashCode():Int; def loc():Place; } </pre> <p> We no longer need to have a <code>Value</code> class or a <code>Ref</code> class. </p> <p>The classes <code>String, Point, <span class="twikiNewLink">ValRail<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/ValRail?topicparent=Main.XtenTwoOhObjectModel" title="Create this topic"><sup>?</sup></a></span>, Region, Dist</code> will not have any local state. The class <code>Rail</code> will have local state. </p> <h2><a name="Examples"></a> Examples </h2> <strong>Example</strong> Assume the class declarations. <p></p> <pre>class C { ...} class D { var f:C=null; } </pre> <p> Now consider the code: </p> <pre>val x = new C(..); // C object o created, reference stored in x. at (P) { // In the body x contains a remote reference to o val f = new D(); f.x1 = x; // remote reference stored in f.x1 Console.OUT.println((f.x1 == x); // must print true Console.OUT.println((x == x); // must print true at (Q) { // x continues to be a remote reference to o1. at (P) { Console.OUT.println(f.x1 == x); // must print true Console.OUT.println((x == x); // must print true } } } </pre> <p> </p> <p><strong>Example</strong> </p> <pre>val x = new C(..); // C object o created, reference stored in x. // type of x is local C{c} if the return type of the constructor is C{c}. at (P) { val x1 = x; // type of x is C{c} because of the place shift introduced by at(P) at (x.loc()) { // x is now bound to o through a local reference. So is x1. Console.out.println(x1==x); // Must print true. // local methods can be invoked on x or x1 and will execute locally on o // type of both x and x1 is local C{c}. } } </pre> <p> </p> <h1><a name="Programming_Methodology"></a> Programming Methodology </h1> <p> A programmer wishing to ensure that a <code>val</code> field is not serialized when the containing object is serialized (e.g. because it contains a large cache which makes sense only in the current place) must mark that field as <code>local</code>. </p> <p></p> <h1><a name="Todo"></a> Todo </h1> <ul> <li> Figure out an explicit <code>global System.copy[T](o:T):T</code> operator. Could also be provided as a method on <code>Object</code> provided that we introduce <code>MyType</code>, i.e. <code>copy():MyType</code>. The <code>copy</code> operator should make a shallow copy of the object. If the object is rooted, this will involve communication to make a copy of the local state of the object. There are no atomicity requirements. </li> </ul> <p> </p> <ul> <li> Check the semantics of <code>as</code> and ensure that it can be performed locally on a proxy. </li> <li> <strike>Figure out how best to make the following claim: "The properties of proxies are such that the programmer is guaranteed that the invocation of a pure global method (with identical arguments) will return identical results on the proxy or the root object."</strike> </li> <li> <strike>Should there be <code>shallowCopy()</code> and <code>deepCopy()</code> methods? </strike> </li> <li> <strike>Figure out with Nate whether we need an <code>= = =</code> or does an <code>==</code> suffice?</strike> </li> </ul> <p></p> <h1><a name="Comments"></a> Comments </h1> <h2><a name="What_s_different_on_X10_2_0_that"></a> What's different on X10 2.0 that impacts the object model? </h2> <ul> <li> In X10 2.0, there's a more complex notion of a remote object/references. Unlike in previous version of X10, activities in a remote place can perform some operations on the object without having to async to the objects home place first. In particular, the following operations are now allowed in non-local objects: <ul> <li> access to final fields of the object </li> <li> invocation of "global" instance methods on the object (either via a virtual call or via an interface call). </li> <li> perform basic object model operations such as <code>instanceof</code> and <code>as</code> </li> </ul> </li> <li> X10 2.0 does not have X10 1.7's Value types </li> <li> X10 2.0 has user-definable primitives (<a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhPrimitives" class="twikiLink">XtenTwoOhPrimitives</a>). </li> </ul> <p></p> <h2><a name="Background_Remote_Objects_in_X10"></a> Background: Remote Objects in X10 1.7 C++ backend/runtime </h2> In 1.7, remote objects were more or less opaque handles (all that could be done to them was to pass them around and find out what place they were located at). As a result, we were able to use a fairly space efficient tagged union scheme to represent them. If an object was local, then the bottom two bits of the pointer to the object were <code>00</code> and the pointer pointed to an instance of the expected C++ type. If the bottom two bits of the pointer were <code>01</code>, then the pointer pointed to a struct containing the place and address of the object on the remote place. <p></p> <h2><a name="Proposed_X10_2_0_object_model_fo"></a> Proposed X10 2.0 object model for C++ backend/runtime </h2> Core idea: we use real C++ object instances for both local and remote objects. We use the same tagged pointer trick as in 1.7. If the pointer is tagged as remote, then immediately before the object in memory we place the struct containing the home place/address information. Thus, only remote objects pay the space overhead for this. <ul> <li> When deserializer allocates space for the object, it does a single alloc that gets space for both the struct and the object instance. It puts in the struct, adjusts the raw pointer, and then constructs the object and fills in its final fields. </li> <li> In the short run, this can be made to work with C++ virtual inheritance by ensuring the following properties: <ul> <li> Objects are always cast up to <code>Ref</code> before being serialized </li> <li> To access the location of an object that is remote, one must upcast to <code>Ref</code> first (to ensure this pointer is pointing to first word of object) then one can look backwards in memory for the location & remote addr field </li> <li> Before doing dynamic casts, etc one must untag the pointer to allow C++ to access the objects vtable, etc. Retag after done. </li> </ul> </li> <li> Longer term, we get rid of virtual/multiple inheritance at the C++ level and implement interface dispatching ourselves. This gets us to a simpler C++ object model where we don't need dynamic casts and the <code>this</code> pointer will never be adjusted. </li> </ul> <p>Alternatives: </p> <ul> <li> Could put location/remote addr information in a side hashtable instead of adjacent to the object. Less efficient and slower, but less tricky. </li> <li> Could pay space overhead of putting location/remote addr information in all objects (ie, put them in <code>Ref</code>). Simple, but wastes space </li> <li> Could build complex C++ type hierarchy where for a single X10 type <code>T</code> we generate three C++ types (abstract superclass, local subclass, remote subclass). </li> </ul> <p><strong>Vj</strong>: My suggestion is to start with the hashtable implementation -- guaranteed to work. Remote operations are going to be expensive anyway. </p> <ul> <li> If the first scheme is to be tried make sure there are lots of small test cases, and these are run with both xlc and gcc on all the architectures of interest to us. Dont see any reason for the complex C++ hierarchy. You will face the problem that X10 subclass relations are not represented by C++ subclass relations. </li> </ul> <strong>Vj</strong> (06/23): Idle thought: Wonder whether there is a way to create proxy objects without allocating all the extra space for local fields. I wonder whether an X10 class <code>A</code> (extending <code>B</code>) could be implemented with two classes <code>AWhole</code> (has all the state) and <code>AProxy</code> (only has global state), with <code>AWhole</code> inheriting from <code>BWhole</code> and <code>AProxy</code>? (<code>AProxy</code> inherits from <code>BProxy</code>.) In the C++ translation, the type <code>A</code> is translated to <code>AProxy</code>. Local methods are invoked on <code>AProxy</code> after casting it to <code>AWhole</code>. <ul> <li> <strong>IP</strong> We said that the global fields will be available remotely, but we have never promised that accessing them would be as efficient as doing the same locally. So, the compiler could generate special remote accessor methods for global fields that would fetch the field on-demand and store it in newly allocated storage. That way, proxies could be as large or as small as we would want to make them... The downside is that we would have to generate all of those extra classes and code. </li> </ul> <p></p> <h2><a name="Language_Semantics_Questions"></a> Language Semantics Questions </h2> <ul> <li> What is the expectation for <code>==</code> on remote objects? <strong>VJ: See above.</strong> <ul> <li> Codegen for equals to handle both local case inline and other cases outofline? </li> <li> Must we canonicalize the object in remote place? Ie, since all of the state is final, can we just allow there to be multiple copies of the object in a place as long as we define equals appropriately? <ul> <li> NN: yes, <code>==</code> on remote refs can compare the location/remote addr, not the pointer to the cached payload. Note we must be careful to not use remote refs for local objects. </li> <li> <strike>VJ: Hmm. I dont really understand what NN wrote above. <code>==</code> is always reference equality. If the references are proxies, reference equality implies root equality. But two proxies may be not equal and still have equal roots.</strike> <strong>Changed to require that <code>==</code> compare the remote address.</strong> </li> </ul> </li> </ul> </li> <li> <strike>Can the programmer control (via annotations?) that some final fields are actually not to be sent to the remote place? Ie, the notions of val/var and global/local may need to be separated for network efficiency. </strike> <ul> <li><strike> NN: Fields could be annotated <code>@Nontransferable</code> (alternatives: <code>@Fixed</code>, <code>@Immovable</code>). This is perhaps only useful if the cached payload in the remote proxy is sparse; that is, if we use a different C++ class for remote objects, eliding the var fields, and local objects and generate different field access code. Field access code for nontransferable fields will be expensive (but probably not much compared to the communication needed to retrieve the contents). </strike></li> <strike> </strike> <li><strike> VJ: It is reasonable to assume that the programmer should be able to control which fields of a class are serialized on a remote reference. Hence the introduction of <code>local</code> above.</strike> <strong>The <code>local</code> annotation on fields resolves this issue.</strong> </li> </ul> </li> </ul> <p></p> <h2><a name="Do_we_need_mobile_objects"></a><a name="Do_we_need_mobile_objects_"></a> Do we need mobile objects? </h2> <p> Version 1.3 gets rid of the mobile objects of Version 1.2 (see earlier versions of this page to get at the Version 1.2 description). </p> <p>Igor notes that: </p> <ul> <li> <strong>IP</strong>: In particular, this means that transmitting any <span class="twikiNewLink">ValRail<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/ValRail?topicparent=Main.XtenTwoOhObjectModel" title="Create this topic"><sup>?</sup></a></span> or closure will incur the remote reference overhead. </li> </ul> <p>This is correct. In essence, the suggestion is that introducing the notion of mobile objects -- over and above the <code>val</code> kernel that will be transmitted for the object anyway -- is a premature optimization that complicates the object model for not much gain. </p> <p>There are several points to make: </p> <ul> <li> If you really truly do not want to send the local reference over when serializing the <code>val</code> state of the object, then consider using a primitive instead of an object. Note this is not a like-for-like change: primitives come with significant restrictions. <ul> <li> Closures should be implemented as opaque primitives (with no member fields). This means we may want to consider permitting generic primitives. Note that the implementation of an <code>async</code> requires the transmission of an implementation-level closure. There should be no reason to transmit the local reference with such an <code>async</code>. In fact one should not, in order to get performance for <code>RandomAccess</code>. </li> <li> We prolly want to introduce <span class="twikiNewLink">OpenCL<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/OpenCL?topicparent=Main.XtenTwoOhObjectModel" title="Create this topic"><sup>?</sup></a></span> vectors as primitives in the language. </li> </ul> </li> <li> What are good examples of programs in which <code>ValRail</code> or <code>Point</code> cross place boundaries in a performance-efficient context? They do in <code>NQueens</code> and <code>UTS</code>, but communication is not the bottleneck there. </li> <li> If the compiler can establish that the deserialized object is not subject to <code>&63;&63;</code> then it does not need to transmit the local reference. This may be possible to do for programs which keep the deserialized object on the stack. </li> <li> The implementation doesnt really need to transmit all the bits for the local reference. It could transmit, for instance a run-length-encoded unique id. e.g. keep a table that maps indices to local references, and send out only the indices. Indices are run-length encoded. That is, you send a byte that encodes the number of bytes, followed by that many bytes. </li> </ul> <p></p> <p></p> <h2><a name="Fields_vs_properties"></a> Fields vs. properties </h2> <p> </p> <ul> <li> NN: I wanted to remove properties, and just have programmers just use final fields in constraints. The objection was that there can be cycles among final fields, but not cycles among properties for decidability reasons. But, could we just not allow cycles of final fields? I don't see that they're that useful and I would rather eliminate a mostly redundant concept from the language. <ul> <li> VJ: (I think you mean <code>public final</code> instance fields should not be distinguished from properties.) I think removing the ability to have final cyclic structures would be a significant restriction -- you do not want to impose restrictions on what is possible to do with object structures in the heap. e.g. you create a fixed (immutable) data flow graph through which you pump data via connectors. Not being able to create such a graph, or requiring that the edge relation must be mutable would be unfortunate, particularly given the emphasis in immutability as a way to get determinacy. Also, see the discussion of <code>local</code>. It makes sense to have <code>local</code> final fields, but not <code>local</code> properties. The key point is to think of properties as capturing some public aspect of the object that can be used in categorization (i.e. in the type system) and is hence part of the public specification of the object as opposed to part of the implementation. </li> </ul> </li> </ul> -- <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/DaveGrove" class="twikiLink">DaveGrove</a> - 01 Jun 2009 <p></p> <hr> <h1><a name="History"></a> History </h1> <p> </p> <ul> <li> Version 1.4. Removed section on primitives. The 1.4 proposal automatically defines a <code>struct</code> for each class with only <code>val</code> fields. Structs have their own inheritance hierarchy, but only exact types are permitted at structs. Unconstrained generic variables can be instantiated with either structs or class types. </li> </ul> <p> </p> <ul> <li> Adopted another suggestion from Cunningham, seconded by Nate, to consider all classes to be <code>rooted</code>. Hence, no mobile objects. No need for the <code>rooted</code> qualifier. So to create a remote reference the implementation must serialize enough information to encode a local reference to the object. Main advantage: Simplifies object model. You can only create objects which can have remote references and which use rooted equality for <code>??</code>. If you want to use structural equality, use primitives. Version 1.2 may be found at <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhObjectModel?rev=13" target="_top">rev 13</a> </li> <li> Introduced the <code>rooted</code> qualifier on classes. A class must be declared <code>rooted</code> if it has any local state. </li> <li> Version 1.1 of the design may be found at <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhObjectModel?rev=10" target="_top">rev 11</a> of this page. <ul> <li> Changed semantics of <code>==</code> so that it behaves like <code>System.equalRoots(..)</code>. This simplifies things considerably. No need to talk of proxies explicitly. </li> </ul> </li> <li> Version 1.0 of the design may be found at <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhObjectModel?rev=10" target="_top">rev 10</a> of this page. </li> <li> Change from Version 1.0 <ul> <li> Eliminated <code>global</code> classes. </li> <li> Removed any connection between inheritance and <code>local</code> state ... any class may have <code>local</code> state even if its super-class or sub-class does not. Primitives do not have <code>local</code> state. </li> <li> In particular, there is no longer a <code>loc():Place</code> method on <code>Object</code>. </li> <li> As before, <code>local</code> state may be mutable or immutable. </li> <li> Eliminate <code>valEquals</code> and any discussion of <code>equals</code>. It is up to the programmer to define <code>equals</code>, as in Java (obeying the contract to keep it consistent with <code>hashCode</code>.) </li> </ul> </li> <li> Dave C is proposing that <code>p==q</code> be defined as <code>System.rootEquals(p,q)</code>. The idea is that this is more robust. I think this change can be made below without changing anything else. It essentially means that proxies behave as if they are interned -- there is no way for an X10 computation to figure out then that <code>p</code> and <code>q</code> are two distinct (i.e. <code>!=</code>) objects but point to the same root. </li> </ul> <br> <ul> <li> Initially created: 6 July 2009 </li> <li> Last updated: vj 6 July 2009 </li> <li> Base link: <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign" class="twikiLink">XtenTwoOhDesign</a> </li> </ul> <p></p> <h1><a name="User_defined_primitives_Version"></a><a name="User_defined_primitives_Version_"></a> User-defined primitives (Version 1.5) </h1> <p> An instance of a class <code>C</code> (an <em>object</em>) is represented in X10 as a contiguously allocated chunk of words in the heap, containing the fields of the object as well as one or more words used in method lookup. Variables with base type <code>C</code> (or a supertype of <code>C</code>) are implemented as cells with enough memory to hold a <em>reference</em> to the object. The size of a reference (32 bits or 64 bits) depends on the underlying operating system. </p> <p>For many high-performance programming idioms, the overhead of one extra level of indirection represented by an object is not acceptable. For instance, a programmer may wish to define a type <code>complex</code> (consisting of two <code>double</code> fields) and require that instances of this type be represented precisely as these two fields. A variable or field of type <code>complex</code> should, therefore, contain enough space to store two <code>doubles</code>. An array of <code>complex</code> of size <code>N</code> should store <code>2*N</code> doubles. Parameters of type <code>complex</code> should be passed inline to a method as two doubles. If a method's return type is <code>complex</code> the method should return two doubles on the stack. Two values of this type should be equal precisely when the two doubles are equal (structural equality). </p> <p></p> <h2><a name="Structs"></a> Structs </h2> X10 supports user-defined primitives (called <em>structs</em>). A distinguishing characteristic of X10 is that code for structs is generated automatically from code for classes, and structs themselves are organized in an inheritance hierarchy. <ul> <li> For C/C++ programmers, an X10 struct corresponds to a C/C++ struct all of whose fields are <code>const</code>. Such structs do not have any aliasing issues. </li> </ul> <p>The size of a variable of type <code>struct C</code> is the size of the fields defined at <code>C</code> (upto alignement considerations). No extra space is allocated for a vtable or an itable. </p> <p></p> <h2><a name="Definition_of_a_struct"></a> Definition of a struct </h2> Consider a class <pre>Modifiers class C[X1,..., Xn](p1:T1,..., pn:Tn){c} extends B{d} implements I1, ..., Ik { Body } </pre> <p> all of whose fields are <code>val</code> fields. For such a class, X10 automatically defines: </p> <p></p> <pre>Modifiers struct C[X1,..., Xn](p1:T1,..., pn:Tn){c} extends struct B{d} implements I1, ..., Ik { Body' } </pre> where <code>Body'</code> is defined as follows. <ul> <li> The fields of <code>Body'</code> are those of <code>Body</code>. </li> <li> Say that a constructor or method for class <code>C</code> is <code>open</code> if it uses <code>this</code> in a way that distinguishes between the types <code>C</code> and <code>struct C</code>. Specifically, the constructor or method must be marked open if: <ul> <li> It passes <code>this</code> into an argument of type <code>C</code> (or a supertype) </li> <li> It returns <code>this</code> from a method those return type is <code>C</code> (or a supertype). </li> <li> It assigns <code>this</code> to a variable whose type is <code>C</code> (or a supertype). </li> </ul> </li> <li> We require that methods in which <code>this</code> is open are marked <code>open</code>. </li> <li> We require that <code>open</code> methods can be overridden only by <code>open</code> methods. </li> <li> Methods that are not marked <code>open</code> are said to be <em>closed</em>. Closed methods can typecheck with <code>this: struct C</code> as well as <code>this:C</code>. </li> <li> <code>Body'</code> contains precisely the constructors and instance methods of <code>Body</code> which are closed. (Thus <code>struct C</code> may beabstract= and may contain <code>abstract</code> methods.) </li> <li> <code>Body'</code> does not contain any instance initializers. </li> <li> <code>Body'</code> does not contain any static fields or initializers. </li> </ul> <p>Note that in <code>Body'</code> the type of <code>this</code> is <code>struct C</code>. </p> <p><strong>Implementation Note</strong>. The compiler may implement the methods of <code>struct C</code> by statically resolving all <code>this</code> and <code>super</code> calls, and translating the methods of <code>C</code> into static methods. Thus an instance of <code>struct C</code> does not need a vtable or an itable, at the cost of code expansion. </p> <p>Values of a <code>struct C</code> type can be created by invoking a constructor defined in <code>struct C</code>, but without prefixing it with <code>new</code>. </p> <p></p> <h3><a name="Struct_equality"></a> Struct equality </h3> <p> Unlike objects, structs do not have global identity. Instead, two structs are equal <code>==</code> if and only if their corresponding fields are equal <code>==</code> (this is the central property of structs). This implies that a variable of type <code>struct C</code> can contain only values of type <code>struct C</code> -- and not values of type <code>struct D</code> (for some subclass <code>D</code> of <code>C</code>). </p> <ul> <li> Otherwise we would be in the untenable position that two values are considered equal at a type <code>struct C</code> but not at a type <code>struct D</code>, where <code>D</code> is a subtype of <code>C</code>. </li> </ul> <p>We say that <em>struct types are exact types</em> to mean that a variable declared at a struct type must take on precisely the values of that type (and not a subtype). </p> <p></p> <h3><a name="Subtyping_of_structs"></a> Subtyping of structs </h3> <p> The subtype relationship on structs is obtained from the subtype relationship of the underlying classes. If <code>C</code> is a subclass of <code>B</code>, then <code>struct C</code> is a subtype of <code>struct B</code>. If <code>C</code> implements the interface <code>I</code>, then <code>struct C</code> implements the interface <code>struct I</code>. </p> <ul> <li> Variables cannot be declared at the type <code>struct I</code>, for <code>I</code> an interface. Such types are of use in placing bounds on type variables. </li> </ul> <p>Since <code>struct</code> types are exact types, the primary use of the subtype relationship on <code>structs</code> is in imposing constraints on type parameters (e.g. <code>X <: struct C</code>). The constraint <code>X <: Object</code> can be used to ensure that <code>X</code> can only be instantiated with classes. Similarly, <code>X <: struct Object</code> ensures that <code>X</code> can only be instantiated with structs. </p> <p></p> <h2><a name="Conversions"></a> Conversions </h2> <p> For <code>D</code> a subclass of <code>C</code> we permit a value <code>v</code> of type <code>struct D</code> to be assigned to a variable of type <code>struct C</code> by slicing off the fields from <code>v</code> which are not in <code>C</code>. </p> <p>For <code>v</code> a value of type <code>C</code> or <code>struct C</code>, <code>struct v</code> is the value of type <code>struct C</code> obtained by copying the fields of <code>v</code> (the <em>unboxed</em> version of <code>v</code>). </p> <p>For <code>v</code> a value of type <code>struct C</code> or <code>C</code>, <code>new v</code> is a value of type <code>C</code> whose fields are initialized with the values of the fields of <code>v</code> (the <em>boxed</em> version of <code>v</code>.) </p> <p> </p> <ul> <li> Note that the implementation may perform some form of interning. Hence there is no guarantee that <code>new</code> will actually create a new object. This does not create any aliasing issues since <code>struct C</code> has no mutable state. </li> </ul> <p>Even if <code>C</code> implements an interface <code>I</code>, a value <code>v</code> of type <code>struct C</code> cannot be assigned to a variable <code>x</code> of type <code>I</code>. However, the boxed version, <code>new v</code> can be. </p> <p></p> <h2><a name="Generic_programming_with_structs"></a> Generic programming with structs </h2> <p> An unconstrained type variable <code>X</code> can be instantiated with subtypes of <code>Object</code> (classes) or subtypes of <code>struct Object</code> (structs). Within a generic class, all operations are available on a variable of <code>X</code>. For instance, variables of <code>X</code> may be used with <code>==</code>, <code>!=</code>, <code>instanceof</code>, casts etc The programmer must be aware of the different interpretations of equality for structs and classes and ensure that the code is correctly written for both cases. </p> <p></p> <p></p> <h1><a name="TODO"></a> TODO </h1> <ul> <li> Explore whether it makes sense to permit the programmer to define a <code>struct</code> directly. It should be possible to define a <code>class</code> from the <code>struct</code> in a similar manner. </li> <li> Consider marking fields as <code>open</code>. Such fields are not copied to <code>struct C</code>. Closed methods cannot access <code>open</code> fields. (Need good use case for such fields.) </li> </ul> <p></p> <h1><a name="FAQ"></a> FAQ </h1> <p> </p> <ul> <li> Why is <code>struct C</code> defined only when <code>C</code> has no <code>var</code> fields? _Because this means that there are no aliasing issues to consider. Otherwise it would be necessary to introduce an address-of operator see <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhInlined" class="twikiLink">XtenTwoOhInlined</a>). This complicates the story on generating the code for the <code>struct C</code> automatically from <code>C</code>, and on instantiating generic code with <code>C</code> and <code>struct C</code>. </li> </ul> <p></p> <h1><a name="Comments"></a> Comments </h1> <p> </p> <p></p> <h1><a name="History"></a> History</h1> <br> <br> </body> </html> |
From: David C. <dc...@us...> - 2009-07-07 00:46:31
|
> Yes, tagged unions are orthogonal. > > The proposal is really saying that if we focus on permitting inlined T > only when T's fields are all val then there is no difference with the > primitives proposal. But that is just a side comment, it doesnt change > the proposal. There are some remaining differences, nothing to do with tagged unions: * The programmer has to decide in advance if a class is ever to be inlined, and define it as a primitive instead of as a class. As such, one cannot just inline a 3rd-party library class if they feel like it. * We will find it easier to omit vtable pointers in the first iteration (and therefore will not need to special-case int for November). > The main additional point in the 1:36 pm message is that in fact we can > support struct T for those T which are not final as long as we insist > that all the struct T types are exact. (I believe Olivier and Nate knew > this/proposed this as well.) This makes it easier to support classes > which are generic on either classes or structs. Yes, this is pretty obvious I think. If you followed that discipline when programming C++ then you would get this behaviour. I think there is only one thing that has been proposed, that is not supported directly by C++ in some way. This is why most of what has been proposed has compiled down to C++ very easily. The one thing I can think of: We want to support the omission of vtable pointers from inlined instances of classes. C++ doesn't need this because in C++ the programmer can define classes that do not have any virtual functions, thus have no vtables. In X10 this would not be possible since every class has at least the virtual functions inheritted from Object. You could argue C++ has something like the primitives proposal, but it is a lot more powerful since its primitives can have mutable fields, and support inheritance. If we are *not* getting rid of the vtable pointer from inlined instances of classes, and inlineable classes must be immutable (this I believe is the proposal for November), then there is nothing in that proposal that is not supported directly by C++. C++ is more powerful, however, since it supports inlining of mutable classes. There are other C++ features that these proposals could potentially support but I don't think they would give a big boost to expressive power. > The main thing you need to watch out for as an X10 programmer is that if > you write a generic class such as: > > typedef Pair[X,Y](x:X,y:Y)= Pair[X,Y]{self.x==x, self.y==y}; > class Pair[X,Y] (x:X,y:Y){ > def this(x:X, y:Y): Pair[X,Y](x,y) { > property(x,y); > } > def x()=x; > def y()=y; > } > > which has fields defined at generic types, then the behavior of the > class instantiated on structs is going to be slightly different from the > behavior of the class instantiated on classes. e.g. > > typedef a = struct A; > val a:A = ...; > val b:B = ...; // assume B extends A > val ab1 = new Pair[A,A](b,b); // ok > val ab2 = new Pair[A,A](a,a); // ok > val ab3 = new Pair[a,a](b, b); // not ok. > val ab4 = new Pair[a,a](a,a); // ok > This is identical behaviour to C++ except the ab3 version is allowed in C++ (it slices). So C++ is a superset of X10. Arguably the slicing is not very useful, so I would be in favour of disallowing it as a type error. If the programmer actually wants to slice, they can fill in the fields manually. I think the loss of mutable fields in inlined instances is a much more significant difference between C++ and X10. For curiosity, here is the C++ version of the above code: template<class X, class Y> class Pair { public: X x; Y y; Pair (X x_, Y y_) : x(x_), y(y_) { } X getX() { return x; } Y getY() { return y; } }; class A { int f; }; class B : public A { int g; }; int main(void) { A *a = new A(); B *b = new B(); Pair<A*,A*> *ab1 = new Pair<A*,A*>(b,b); // ok Pair<A*,A*> *ab2 = new Pair<A*,A*>(a,a); // ok Pair<A,A> *ab3 = new Pair<A,A>(*b,*b); // not ok. (works in c++, discards g field) Pair<A,A> *ab4 = new Pair<A,A>(*a,*a); // ok } |
From: Vijay S. <vi...@sa...> - 2009-07-06 22:37:35
|
Yes, tagged unions are orthogonal. The proposal is really saying that if we focus on permitting inlined T only when T's fields are all val then there is no difference with the primitives proposal. But that is just a side comment, it doesnt change the proposal. The main additional point in the 1:36 pm message is that in fact we can support struct T for those T which are not final as long as we insist that all the struct T types are exact. (I believe Olivier and Nate knew this/proposed this as well.) This makes it easier to support classes which are generic on either classes or structs. The main thing you need to watch out for as an X10 programmer is that if you write a generic class such as: typedef Pair[X,Y](x:X,y:Y)= Pair[X,Y]{self.x==x, self.y==y}; class Pair[X,Y] (x:X,y:Y){ def this(x:X, y:Y): Pair[X,Y](x,y) { property(x,y); } def x()=x; def y()=y; } which has fields defined at generic types, then the behavior of the class instantiated on structs is going to be slightly different from the behavior of the class instantiated on classes. e.g. typedef a = struct A; val a:A = ...; val b:B = ...; // assume B extends A val ab1 = new Pair[A,A](b,b); // ok val ab2 = new Pair[A,A](a,a); // ok val ab3 = new Pair[a,a](b, b); // not ok. val ab4 = new Pair[a,a](a,a); // ok David Cunningham wrote: > > > > > Already sent design in my message of 1:36 pm. > > Oops yes I should read all the emails, and then reply, rather than > read/reply each message individually :) > > I don't understand why you want to go back to the primitives concept. > It seems to me that tagged unions are orthogonal to the choice of > primitives/inlined classes. It should be possible to implement them > as a syntax sugar either way. > |
From: Igor P. <ig...@us...> - 2009-07-06 22:28:58
|
David P Grove/Watson/IBM@IBMUS wrote on 07/06/2009 05:54:37 PM: > The return using the statement expression was a red herring. The > actual bug appears to be in type inference for the closing "e" of > (__extension__ ({ s1; s2; e })); when e is a fairly complex expression. > > If e is a simple variable reference (which can be achieved by > rewriting to: (__extension__ ({ s1; s2; typeof(e) tmp = e; tmp })); ) > then it looks like g++ 4.1 can handle it. Great. I'll commit something to that effect later tonight or tomorrow. Thanks for the analysis. Igor -- Igor Peshansky (note the spelling change!) IBM T.J. Watson Research Center XJ: No More Pain for XML's Gain (http://www.research.ibm.com/xj/) X10: Parallel Productivity and Performance (http://x10.sf.net/) |
From: David C. <dc...@us...> - 2009-07-06 22:20:07
|
> > Already sent design in my message of 1:36 pm. Oops yes I should read all the emails, and then reply, rather than read/reply each message individually :) I don't understand why you want to go back to the primitives concept. It seems to me that tagged unions are orthogonal to the choice of primitives/inlined classes. It should be possible to implement them as a syntax sugar either way. |
From: David P G. <gr...@us...> - 2009-07-06 21:55:31
|
The return using the statement expression was a red herring. The actual bug appears to be in type inference for the closing "e" of (__extension__ ({ s1; s2; e })); when e is a fairly complex expression. If e is a simple variable reference (which can be achieved by rewriting to: (__extension__ ({ s1; s2; typeof(e) tmp = e; tmp })); ) then it looks like g++ 4.1 can handle it. --dave |
From: Vijay S. <vi...@sa...> - 2009-07-06 21:43:00
|
Already sent design in my message of 1:36 pm. David Cunningham wrote: > > > You can obviously write a TaggedUnion (or a Variant) in X10 for any > > given (fixed) set of types. I believe what Vijay was asking was how > > does one write a *generic* TaggedUnion class in X10. > > I don't know of any language supports this, nor can I think of a use > case for it. > > Many languages have a simple construct that defines a type that is a > union of a fixed set of types. Essentially the same as the X10 code I > gave but a lot more concise. We can obviously add such a construct to > the language and use a front-end desugarer pass to expand it out as I > just did manually. > > Not sure it's worth the effort for such a fringe requirement though. > |
From: David C. <dc...@us...> - 2009-07-06 20:25:06
|
> You can obviously write a TaggedUnion (or a Variant) in X10 for any > given (fixed) set of types. I believe what Vijay was asking was how > does one write a *generic* TaggedUnion class in X10. I don't know of any language supports this, nor can I think of a use case for it. Many languages have a simple construct that defines a type that is a union of a fixed set of types. Essentially the same as the X10 code I gave but a lot more concise. We can obviously add such a construct to the language and use a front-end desugarer pass to expand it out as I just did manually. Not sure it's worth the effort for such a fringe requirement though. |
From: Igor P. <ig...@us...> - 2009-07-06 20:20:25
|
Dave, This is unfortunate, although, if it's only return that is affected, we don't need a general expression flattener -- simply turning return (__extension__ ({ s1; s2; e })); into typeof(e) t = (__extension__ ({ s1; s2; e })); return t; should suffice. We ought to mark this change as a workaround for a compiler bug. I do agree that if we find more such cases, it might make sense to work on a flattener. However, I don't think we should expend a lot of effort working around compiler bugs, if the solution is to upgrade... We've already run into such problems with xlC. Igor David P Grove/Watson/IBM@IBMUS wrote on 07/06/2009 03:58:33 PM: > I think we may need to rethink our reliance on statement expressions > for C++ code generation. This snippet of code: > > return (__extension__ ({ > x10aux::ref<x10::util::Builder<x10_byte, x10__T> > _ = ((x10aux:: > ref<x10::io::ByteWriter<x10__T> >)this)->x10__b; > ((x10::util::_itable_Builder<x10_byte, x10__T>*)x10aux:: > findITable<x10::util::Builder<x10_byte, x10__T> >(_))->result(_); > }) > ); > > breaks g++ version 4.1.2 (aka the version of g++ that is installed by > default on Red Hat Enterprise Linux 5.x) with an internal compiler > error, while this snippet (the same, without the statement expression) > works just fine. > > x10aux::ref<x10::util::Builder<x10_byte, x10__T> > _ = ((x10aux:: > ref<x10::io::ByteWriter<x10__T> >)this)->x10__b; > return ((x10::util::_itable_Builder<x10_byte, x10__T>*)x10aux:: > findITable<x10::util::Builder<x10_byte, x10__T> >(_))->result(_); > > I've spent some time tweaking what is inside of the statement > expression without making the crash on g++ 4.1.2 go away. It seems to > be some interaction between return and statement expression that is > fairly insensitive to the exact details of the actual statement > expression. The problem code does compile with g++ 4.3.2, however this > is unlikely to be the default version of g++ on many of our user's machines. > > Note, the only reason the code generator is attempting to use a > statement expression here is that we haven't flattened the arguments > to method call expressions. If we knew that all actual arguments to a > method were simple variable references and/or constants then we > wouldn't need to protect ourself against double evaluation of the > receiver expression by using a statement expression. > > But, there's a more general problem here as well. If statement > expressions are bug prone on commonly deployed versions of g++, then > we may not be able to rely on using them to support code generation of > inlined methods. To handle the general case of inlining, we may need a > general expression flattener. > > --dave -- Igor Peshansky (note the spelling change!) IBM T.J. Watson Research Center XJ: No More Pain for XML's Gain (http://www.research.ibm.com/xj/) X10: Parallel Productivity and Performance (http://x10.sf.net/) |
From: David P G. <gr...@us...> - 2009-07-06 19:58:55
|
I think we may need to rethink our reliance on statement expressions for C+ + code generation. This snippet of code: return (__extension__ ({ x10aux::ref<x10::util::Builder<x10_byte, x10__T> > _ = ((x10aux::ref<x10::io::ByteWriter<x10__T> >)this)->x10__b; ((x10::util::_itable_Builder<x10_byte, x10__T>*)x10aux::findITable<x10::util::Builder<x10_byte, x10__T> >(_))-> result(_); }) ); breaks g++ version 4.1.2 (aka the version of g++ that is installed by default on Red Hat Enterprise Linux 5.x) with an internal compiler error, while this snippet (the same, without the statement expression) works just fine. x10aux::ref<x10::util::Builder<x10_byte, x10__T> > _ = ((x10aux::ref<x10::io::ByteWriter<x10__T> >)this)->x10__b; return ((x10::util::_itable_Builder<x10_byte, x10__T>*)x10aux::findITable<x10::util::Builder<x10_byte, x10__T> >(_))-> result(_); I've spent some time tweaking what is inside of the statement expression without making the crash on g++ 4.1.2 go away. It seems to be some interaction between return and statement expression that is fairly insensitive to the exact details of the actual statement expression. The problem code does compile with g++ 4.3.2, however this is unlikely to be the default version of g++ on many of our user's machines. Note, the only reason the code generator is attempting to use a statement expression here is that we haven't flattened the arguments to method call expressions. If we knew that all actual arguments to a method were simple variable references and/or constants then we wouldn't need to protect ourself against double evaluation of the receiver expression by using a statement expression. But, there's a more general problem here as well. If statement expressions are bug prone on commonly deployed versions of g++, then we may not be able to rely on using them to support code generation of inlined methods. To handle the general case of inlining, we may need a general expression flattener. --dave |
From: Vijay S. <vi...@sa...> - 2009-07-06 17:37:36
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type"> <title></title> </head> <body bgcolor="#ffffff" text="#000000"> Some more thoughts.<br> <ul> <li>A disadvantage of the separate primitives + classes proposal is that from a primitive P there is no way of getting a class which has the same data elements as P and the same methods as P. (<tt>Nullable[P]</tt> does not suffice.) Why would we want that? Because variables at an interface type can only be instantiated with objects (not with primitives) -- since they must have size 1, cannot have arbitrary size.<br> </li> <li>So how would the story outlined below be made to work if we permit the user to define only classes? Not difficult:<br> </li> <ul> <li>For every class C (with only <tt>val</tt> fields -- this restriction so we dont have to worry about aliasing issues), we let <tt>struct C</tt> be a new type. This type satisfies the following properties. <br> </li> <ul> <li>If <tt>C <: B</tt>, then <tt>struct C <: struct B</tt>.</li> <li>If <tt>C implements I</tt>, then <tt>struct C implements I</tt>.</li> </ul> <li>Let C be defined by <tt>Modifiers class C[X1,..., Xn](p1:T1,..., pn:Tn){c} extends B{d} implements I1, ..., Ik { Body}. </tt>Then <tt>struct C</tt> is defined as if by <tt>Modifiers struct C[X1,...,Xn](p1:T1,..., pn:Tn){c} extends struct B{d} implements I1,..., Ik { Body'}</tt>.<br> </li> <li>The fields of <tt>struct C</tt> are those of C. <br> </li> <li>The methods of <tt>struct C</tt> are those of <tt>C</tt> that are not marked <tt>ref</tt>. (That is, the definition of such methods are copied into the body of <tt>struct C</tt>. In these methods, the type of <tt>this</tt> is <tt>struct C</tt>.) The compiler throws an error if a method not marked <tt>ref </tt>uses this in an upcast, or assigns this to a variable whose type is an interface or whose type is a supertype of C. (These operations are not possible for <tt>struct C</tt>.)</li> <ul> <li><tt>ref</tt> is the dual of the idea of <tt>closed</tt>. The idea is that the programmer marks those methods as ref which make sense only for C, do not make sense for <tt>struct C</tt>.</li> <li>The compiler should give a warning if this is used with <tt>==</tt> and <tt>!=</tt>, since their semantics is different for <tt>struct C</tt>.<br> </li> <li>In the implementation, the methods of <tt>struct C</tt> are compiled into static methods where all dispatch choices are resolved statically, under the assumption that the type of <tt>this</tt> is exactly <tt>struct C</tt> (not <tt>struct D</tt>, for some subclass <tt>D</tt> of <tt>C</tt>.)<br> </li> <li>Note that if <tt>D extends C</tt>, then the static methods for <tt>struct D</tt> may not be related to the static methods for <tt>struct C</tt>.</li> <li>The main point is that the implementation does not keep state with a value of type <tt>struct C</tt> containing an itable or a vtable. Instead, code is expanded. <br> </li> </ul> <li>Note that if the class <tt>C</tt> is abstract, so is <tt>struct C</tt>. <tt>abstract</tt> methods on class <tt>C</tt> translate to abstract methods on <tt>struct C</tt>. <br> </li> <li>The types <tt>struct C</tt> are all exact. That is, a variable of type <tt>struct C</tt> can contain only instances of <tt>C</tt>, not instances of a class <tt>D</tt> extending <tt>C</tt>. Thus the size of a variable of type <tt>struct C</tt> is the sum of the sizes of the fields of <tt>C</tt>.</li> <ul> <li>This is forced by the defining characteristics of structs that equality means equality of all fields. Hence if v1 and v2 are two values at type struct P, they cannot be instances of classes extending P otherwise their state could be different at the fields introduced in these subclasses while being identical at P. Hence they are not ==, even though from the viewpoint of P they are ==.</li> </ul> <li>A value of type struct C cannot be assigned to a variable of interface type I, even if C implements I. (A value of type C can, of course.) A variable of interface type always has the size of a reference; it always contains a reference (an instance of a subclass of Object).</li> <li>So why introduce subtyping relations on the <tt>struct</tt> types? Because it generalizes the proposal in which <tt>struct</tt> (or <tt>inlined</tt>) could only be applied to final classes. It help in specifying bounds on generic types. e.g. <tt>def add[T](x:T, y:T){T <: struct Arithmetic} = x + y; </tt>Note the variables x and y are not being defined at type <tt>Arithmetic</tt>; they are being defined at some <tt>struct</tt> type T which extends <tt>Arithmetic</tt>. <br> </li> <ul> <li>Similarly, we can define a <tt>Comparable[X,Y] </tt>interface, have <tt>int </tt>implement <tt>Comparable[int,int]</tt>, and then instantiate <tt>SortedList[X] </tt>on <tt>int</tt> (<tt>SortedList[X] </tt>requires that <tt>X</tt> <tt>implements Comparable{X,X]</tt>).</li> </ul> <li>Given a value <tt>v </tt>of type <tt>struct C</tt>, one can obtain an object of type <tt>C</tt> using the operation <tt>new v</tt>. This new object will have space for a guid, as must all objects in X10, and its contents are initialized with the contents of v.</li> <li>Unrestricted type parameters can be instantiated by the types <tt>struct C</tt> (for some <tt>C</tt>). <br> </li> <li>If a type parameter <tt>X</tt> has a constraint <tt>X <: struct I</tt>, where <tt>I</tt> an interface, then <tt>X</tt> can be instantiated by a <tt>struct C</tt>, provided that <tt>C <: I</tt>. If <tt>X</tt> has a constraint <tt>X <: struct C</tt>, where <tt>C is </tt>a class, then <tt>X</tt> can be instantiated by a <tt>struct B</tt>, provided that <tt>B <: C</tt>. </li> </ul> </ul> It probably makes sense to permit the programmer to directly define <tt>struct C </tt>(in addition to having structs automatically defined from classes which have only final fields). In such a case it would also probably make sense to automatically define a class C based on the definition of the struct. <br> <br> Now, returning to unions. Instead of thinking of tagged unions as a kind of struct definition, we could in fact think of tagged unions at the level of fields. Then tagged unions would make sense when defining fields of structs or fields of classes. Here is how one would do it. In a class or struct C <br> <br> <tt>[var] <name> : union {<br> case i: Type0<br> case i+1: Type1<br> ...<br> case i+n-1: Typen-1<br> } [Initializer];</tt><br> <br> can be used to declare a tagged union <tt>var</tt> or <tt>val </tt>field. The base types of all the Typei must be different. <br> <br> This implicitly declares two fields, <name>.tag and <name>.value, with the first taking on the values i,...i+n-1, and the second taking a value of the corresponding type. Such values can be operated upon by a typecase as before. One should be able to use a typdef to define union types as well;<br> <br> <tt>typedef javaPrimitive = union { case 0: boolean case 1: int ....};</tt><br> <br> <tt>val x: javaPrimitive = 1; // the value of tag is inferred from the value of the initializer</tt><br> <br> If we were to go down this path, it would prolly make sense to have a subtyping relation on union types U1 <: U2 if U1 has all the summands of U2, and may have more. Then a value of type U1 can be assigned to a variable of type U2.<br> <br> The typecase statement and expression can be used to operate on such values:<br> <br> <tt>typecase (x) {<br> case i: ... // here x has type Type0 <br> case i+1: ...<br> case i+n-1: ...<br> }</tt><br> <br> <br> <br> Vijay Saraswat wrote: <blockquote cite="mid:4A5...@sa..." type="cite"> <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type"> You do mean that an instance of Variant will have space for the tag and one of its summands, not all of them.<br> <br> Yes, this could be made to work in a typesafe manner in X10. And this does provide a good resolution to the question of whether to special case int, uint etc.<br> <br> Here are the outlines -- this is a classes + primitives proposals, where primitives can be unions.<br> <ul> <li>Realize that with the restriction that only those types can be inlined which are final and whose fields are immutable, we are back in the domain of what I called "primitive" a month ago.</li> <li>The main point of a primitive definition (in shape it looks just like a class definition) is that equality is defined on it component-wise (and it is a final class, and it inherits from no class at all, and it has only final members). This is really the only reason for introducing the concept of primitives distinct from classes: Equality is defined differently.<br> </li> <li>We dont need Any, Object can be the top of the hierarchy. However, the type system is not unified. It has Object and all its subclasses and then it has an arbitrary number of primitives which are all "top-level". <br> </li> <li>Unrestricted type parameters can be instantiated by Object and its subclasses or by primitives.</li> <li>int, uint etc are all defined in the x10.lang package as primitives. (We will prolly define complex in x10.lang as well as a primitive).<br> </li> <li>Thus the types ValRail[int], Rail[byte] etc. make sense. ValRail[Object] cannot contain an int of course. <br> </li> <li>Primitives can be embedded into Object through the user-defined class Nullable[X].</li> <li>We flip the bit on "inlined". That is, instead of saying we have a class C and we can obtain from it inlined C (provided that C satisfies certain conditions), we say that the body of a primitive C defines what we have called "inlined C". To obtain a C, use Nullable[C]. Thus, all the primitives can be embedded into the type hierarchy below Object using the user-defined class Nullable[X] (given below). No magic!<br> </li> <li>In the body of a primitive definition C, the type of this is C (not Nullable[C]).</li> <li>instanceof, cast, ==, != are all defined on primitives ... however keep in mind that the type hierarchy of primitives is flat (no hierarchy). <br> </li> <li>The amount of space needed for a primitive is the space needed to represent all its fields. There is no need for any guid data. Instances of Nullable[X], like instances of any class should have guid data unless static analysis can establish that equality is never called on that object. (Tough!)</li> </ul> Now within this framework, we can introduce unions as a way of defining new primitives from existing primitives or classes.<br> <br> <tt>[Access modifier] union <name> (tag: int) {<br> data {<br> case 0: <type1><br> case 1: <type2><br> ...<br> case n: <typen><br> }<br> <constructors><br> <methods><br> }</tt><br> <br> All the types are required to be different. An instance is created with the syntax <name>(value), where value must have one of the n+1 types specified by the union <name>. <br> e.g. <br> <br> <tt>union javaPrimitive(tag: int(0..12)) {<br> data {<br> case 0: byte<br> case 1: short<br> case 2: int<br> case 3: long<br> case 4: boolean<br> ...<br> }<br> ...<br> }</tt><br> <br> We also introduce a new statement typecase.<br> <br> <tt>typecase(x) {<br> case 0: Stm0<br> ...<br> case n: Stmn<br> }</tt><br> <br> It is a compile-time error for x not to be of a union type U. The union must have n+1 elements. Within the body of Stmi, x.data has the type of the i'th summand of U. (Not clear whether x or x.data should have the type of the i'th summand.)<br> <br> Equality on unions is equality of the tag + equality of data (i.e. structural equality, hence unions are primitives). <br> <br> Nullable can be instantiated with any primitive, including a union. Hence Nullable[javaPrimitive] makes sense. An instance of Nullable[javaPrimitive] should require 8 bytes to store a javaPrimitive. <br> <br> More details later in the day.<br> <br> Best,<br> Vijay<br> <br> <tt>typedef Nullable[X](v:X) = Nullable[X]{self.v==v};<br> final class Nullable[X](v:X) {<br> def this(v:X): Nullable[X](v) {<br> property(v);<br> }<br> def me(v:X) = (v==this.v)? this : new Nullable[X](v);<br> }<br> </tt><br> Dave Cunningham wrote: <blockquote cite="mid:200...@do..." type="cite"> <pre wrap="">* Vijay Saraswat (<a moz-do-not-send="true" class="moz-txt-link-abbreviated" href="mailto:vi...@sa...">vi...@sa...</a>) wrote: </pre> <blockquote type="cite"> <pre wrap="">how do you write a tagged union in X10..? </pre> </blockquote> <pre wrap=""><!----> This is what I had in mind: (I called it Variant instead. For a laugh. Or maybe because it's a shorter name than TaggedUnion) If o was an Object instead of a Value we would probably do .equals() instead of == Also, HashMap seems not to have an apply(k,v) method, although it does have a put(k,v) method. import x10.io.Console; import x10.util.HashMap; public class Variant { protected val kind: Int; protected var c: Char; public def this (v:Char) { this.c = v; this.kind = 0; } public static operator (v:Char) : Variant = new Variant(v); protected var i: Int; public def this (v:Int) { this.i = v; this.kind = 1; } public static operator (v:Int) : Variant = new Variant(v); protected var f: Float; public def this (v:Float) { this.f = v; this.kind = 2; } public static operator (v:Float) : Variant = new Variant(v); protected var o: Value; public def this (v:Value) { this.o = v; this.kind = 3; } public static operator (v:Value) : Variant = new Variant(v); public def equals (that:Ref) { if (this==that) return true; if (!(that instanceof Variant)) return false; val other = that as Variant; if (kind != other.kind) return false; switch (kind) { case 0: return c == other.c; case 1: return i == other.i; case 2: return f == other.f; case 3: return o == other.o; } assert false : "Kind value unrecognised"; return false; } public def hashCode () { switch (kind) { case 0: return c.hashCode(); case 1: return i.hashCode(); case 2: return f.hashCode(); case 3: return o.hashCode(); } assert false : "Kind value unrecognised"; return 0; } public def toString () { switch (kind) { case 0: return c.toString(); case 1: return i.toString(); case 2: return f.toString(); case 3: return o.toString(); } assert false : "Kind value unrecognised"; return ""; } public static def main (args : Rail[String]) { val map = new HashMap[Variant,Variant](); map.put(3, 3.0f); map.put(3.0f, "str"); map.put("str", 3); Console.OUT.println("map(3) == "+map(3)); Console.OUT.println("map(3.0f) == "+map(3.0f)); Console.OUT.println("map(\"str\") == "+map("str")); } } // vim: shiftwidth=4:tabstop=4:expandtab </pre> </blockquote> <br> </blockquote> <br> </body> </html> |
From: Vijay S. <vi...@sa...> - 2009-07-06 17:35:12
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type"> </head> <body bgcolor="#ffffff" text="#000000"> You do mean that an instance of Variant will have space for the tag and one of its summands, not all of them.<br> <br> Yes, this could be made to work in a typesafe manner in X10. And this does provide a good resolution to the question of whether to special case int, uint etc.<br> <br> Here are the outlines -- this is a classes + primitives proposals, where primitives can be unions.<br> <ul> <li>Realize that with the restriction that only those types can be inlined which are final and whose fields are immutable, we are back in the domain of what I called "primitive" a month ago.</li> <li>The main point of a primitive definition (in shape it looks just like a class definition) is that equality is defined on it component-wise (and it is a final class, and it inherits from no class at all, and it has only final members). This is really the only reason for introducing the concept of primitives distinct from classes: Equality is defined differently.<br> </li> <li>We dont need Any, Object can be the top of the hierarchy. However, the type system is not unified. It has Object and all its subclasses and then it has an arbitrary number of primitives which are all "top-level". <br> </li> <li>Unrestricted type parameters can be instantiated by Object and its subclasses or by primitives.</li> <li>int, uint etc are all defined in the x10.lang package as primitives. (We will prolly define complex in x10.lang as well as a primitive).<br> </li> <li>Thus the types ValRail[int], Rail[byte] etc. make sense. ValRail[Object] cannot contain an int of course. <br> </li> <li>Primitives can be embedded into Object through the user-defined class Nullable[X].</li> <li>We flip the bit on "inlined". That is, instead of saying we have a class C and we can obtain from it inlined C (provided that C satisfies certain conditions), we say that the body of a primitive C defines what we have called "inlined C". To obtain a C, use Nullable[C]. Thus, all the primitives can be embedded into the type hierarchy below Object using the user-defined class Nullable[X] (given below). No magic!<br> </li> <li>In the body of a primitive definition C, the type of this is C (not Nullable[C]).</li> <li>instanceof, cast, ==, != are all defined on primitives ... however keep in mind that the type hierarchy of primitives is flat (no hierarchy). <br> </li> <li>The amount of space needed for a primitive is the space needed to represent all its fields. There is no need for any guid data. Instances of Nullable[X], like instances of any class should have guid data unless static analysis can establish that equality is never called on that object. (Tough!)</li> </ul> Now within this framework, we can introduce unions as a way of defining new primitives from existing primitives or classes.<br> <br> <tt>[Access modifier] union <name> (tag: int) {<br> data {<br> case 0: <type1><br> case 1: <type2><br> ...<br> case n: <typen><br> }<br> <constructors><br> <methods><br> }</tt><br> <br> All the types are required to be different. An instance is created with the syntax <name>(value), where value must have one of the n+1 types specified by the union <name>. <br> e.g. <br> <br> <tt>union javaPrimitive(tag: int(0..12)) {<br> data {<br> case 0: byte<br> case 1: short<br> case 2: int<br> case 3: long<br> case 4: boolean<br> ...<br> }<br> ...<br> }</tt><br> <br> We also introduce a new statement typecase.<br> <br> <tt>typecase(x) {<br> case 0: Stm0<br> ...<br> case n: Stmn<br> }</tt><br> <br> It is a compile-time error for x not to be of a union type U. The union must have n+1 elements. Within the body of Stmi, x.data has the type of the i'th summand of U. (Not clear whether x or x.data should have the type of the i'th summand.)<br> <br> Equality on unions is equality of the tag + equality of data (i.e. structural equality, hence unions are primitives). <br> <br> Nullable can be instantiated with any primitive, including a union. Hence Nullable[javaPrimitive] makes sense. An instance of Nullable[javaPrimitive] should require 8 bytes to store a javaPrimitive. <br> <br> More details later in the day.<br> <br> Best,<br> Vijay<br> <br> <tt>typedef Nullable[X](v:X) = Nullable[X]{self.v==v};<br> final class Nullable[X](v:X) {<br> def this(v:X): Nullable[X](v) {<br> property(v);<br> }<br> def me(v:X) = (v==this.v)? this : new Nullable[X](v);<br> }<br> </tt><br> Dave Cunningham wrote: <blockquote cite="mid:200...@do..." type="cite"> <pre wrap="">* Vijay Saraswat (<a class="moz-txt-link-abbreviated" href="mailto:vi...@sa...">vi...@sa...</a>) wrote: </pre> <blockquote type="cite"> <pre wrap="">how do you write a tagged union in X10..? </pre> </blockquote> <pre wrap=""><!----> This is what I had in mind: (I called it Variant instead. For a laugh. Or maybe because it's a shorter name than TaggedUnion) If o was an Object instead of a Value we would probably do .equals() instead of == Also, HashMap seems not to have an apply(k,v) method, although it does have a put(k,v) method. import x10.io.Console; import x10.util.HashMap; public class Variant { protected val kind: Int; protected var c: Char; public def this (v:Char) { this.c = v; this.kind = 0; } public static operator (v:Char) : Variant = new Variant(v); protected var i: Int; public def this (v:Int) { this.i = v; this.kind = 1; } public static operator (v:Int) : Variant = new Variant(v); protected var f: Float; public def this (v:Float) { this.f = v; this.kind = 2; } public static operator (v:Float) : Variant = new Variant(v); protected var o: Value; public def this (v:Value) { this.o = v; this.kind = 3; } public static operator (v:Value) : Variant = new Variant(v); public def equals (that:Ref) { if (this==that) return true; if (!(that instanceof Variant)) return false; val other = that as Variant; if (kind != other.kind) return false; switch (kind) { case 0: return c == other.c; case 1: return i == other.i; case 2: return f == other.f; case 3: return o == other.o; } assert false : "Kind value unrecognised"; return false; } public def hashCode () { switch (kind) { case 0: return c.hashCode(); case 1: return i.hashCode(); case 2: return f.hashCode(); case 3: return o.hashCode(); } assert false : "Kind value unrecognised"; return 0; } public def toString () { switch (kind) { case 0: return c.toString(); case 1: return i.toString(); case 2: return f.toString(); case 3: return o.toString(); } assert false : "Kind value unrecognised"; return ""; } public static def main (args : Rail[String]) { val map = new HashMap[Variant,Variant](); map.put(3, 3.0f); map.put(3.0f, "str"); map.put("str", 3); Console.OUT.println("map(3) == "+map(3)); Console.OUT.println("map(3.0f) == "+map(3.0f)); Console.OUT.println("map(\"str\") == "+map("str")); } } // vim: shiftwidth=4:tabstop=4:expandtab </pre> </blockquote> <br> </body> </html> |
From: Nathaniel N. <na...@na...> - 2009-07-06 16:32:21
|
I was proposing union types as a way to get polymorphism for throws clauses. Futures could be written: interface Future[T,X]{X <: Throwable} { def force(): T throws X; } and used as: Future[int,IOException | MyException]; with the following subtyping rules: Ti <: T1 | ... | Tn forall i. T <: Ti ------------------- T <: T1 | ... | Ti [Forgive any errors here, I'm doing this from memory.] I wasn't proposing having variables of union type, just to use them for throws clauses and in generics, so haven't thought about tagged unions. Nate On Jul 6, 2009, at 5:36 PM, Igor Peshansky wrote: > Dave, > > You can obviously write a TaggedUnion (or a Variant) in X10 for any > given (fixed) set of types. I believe what Vijay was asking was how > does one write a *generic* TaggedUnion class in X10. > > Nate, what is the current state of the union types proposal? This > is yet another use case for them. Are we definitely not going to > do this here? > Igor > > Dave Cunningham <dc...@do...> wrote on 07/06/2009 01:47:12 AM: > >> * Vijay Saraswat (vi...@sa...) wrote: >>> how do you write a tagged union in X10..? >> >> This is what I had in mind: (I called it Variant instead. For a >> laugh. >> Or maybe because it's a shorter name than TaggedUnion) >> >> If o was an Object instead of a Value we would probably do .equals() >> instead of == >> >> Also, HashMap seems not to have an apply(k,v) method, although it >> does >> have a put(k,v) method. >> >> >> >> import x10.io.Console; >> import x10.util.HashMap; >> >> public class Variant { >> >> protected val kind: Int; >> protected var c: Char; public def this (v:Char) { this.c = v; >> this.kind = 0; } public static operator (v:Char) : Variant = new > Variant(v); >> protected var i: Int; public def this (v:Int) { this.i = v; >> this.kind = 1; } public static operator (v:Int) : Variant = new > Variant(v); >> protected var f: Float; public def this (v:Float) { this.f = v; >> this.kind = 2; } public static operator (v:Float) : Variant = new > Variant(v); >> protected var o: Value; public def this (v:Value) { this.o = v; >> this.kind = 3; } public static operator (v:Value) : Variant = new > Variant(v); >> >> public def equals (that:Ref) { >> if (this==that) return true; >> if (!(that instanceof Variant)) return false; >> val other = that as Variant; >> if (kind != other.kind) return false; >> switch (kind) { >> case 0: return c == other.c; >> case 1: return i == other.i; >> case 2: return f == other.f; >> case 3: return o == other.o; >> } >> assert false : "Kind value unrecognised"; >> return false; >> } >> >> public def hashCode () { >> switch (kind) { >> case 0: return c.hashCode(); >> case 1: return i.hashCode(); >> case 2: return f.hashCode(); >> case 3: return o.hashCode(); >> } >> assert false : "Kind value unrecognised"; >> return 0; >> } >> >> public def toString () { >> switch (kind) { >> case 0: return c.toString(); >> case 1: return i.toString(); >> case 2: return f.toString(); >> case 3: return o.toString(); >> } >> assert false : "Kind value unrecognised"; >> return ""; >> } >> >> public static def main (args : Rail[String]) { >> val map = new HashMap[Variant,Variant](); >> map.put(3, 3.0f); >> map.put(3.0f, "str"); >> map.put("str", 3); >> Console.OUT.println("map(3) == "+map(3)); >> Console.OUT.println("map(3.0f) == "+map(3.0f)); >> Console.OUT.println("map(\"str\") == "+map("str")); >> } >> } >> >> // vim: shiftwidth=4:tabstop=4:expandtab > -- > Igor Peshansky (note the spelling change!) > IBM T.J. Watson Research Center > XJ: No More Pain for XML's Gain (http://www.research.ibm.com/xj/) > X10: Parallel Productivity and Performance (http://x10.sf.net/) > > > ------------------------------------------------------------------------------ > _______________________________________________ > X10-core mailing list > X10...@li... > https://lists.sourceforge.net/lists/listinfo/x10-core |
From: Igor P. <ig...@us...> - 2009-07-06 15:56:03
|
Dave, You can obviously write a TaggedUnion (or a Variant) in X10 for any given (fixed) set of types. I believe what Vijay was asking was how does one write a *generic* TaggedUnion class in X10. Nate, what is the current state of the union types proposal? This is yet another use case for them. Are we definitely not going to do this here? Igor Dave Cunningham <dc...@do...> wrote on 07/06/2009 01:47:12 AM: > * Vijay Saraswat (vi...@sa...) wrote: > > how do you write a tagged union in X10..? > > This is what I had in mind: (I called it Variant instead. For a laugh. > Or maybe because it's a shorter name than TaggedUnion) > > If o was an Object instead of a Value we would probably do .equals() > instead of == > > Also, HashMap seems not to have an apply(k,v) method, although it does > have a put(k,v) method. > > > > import x10.io.Console; > import x10.util.HashMap; > > public class Variant { > > protected val kind: Int; > protected var c: Char; public def this (v:Char) { this.c = v; > this.kind = 0; } public static operator (v:Char) : Variant = new Variant(v); > protected var i: Int; public def this (v:Int) { this.i = v; > this.kind = 1; } public static operator (v:Int) : Variant = new Variant(v); > protected var f: Float; public def this (v:Float) { this.f = v; > this.kind = 2; } public static operator (v:Float) : Variant = new Variant(v); > protected var o: Value; public def this (v:Value) { this.o = v; > this.kind = 3; } public static operator (v:Value) : Variant = new Variant(v); > > public def equals (that:Ref) { > if (this==that) return true; > if (!(that instanceof Variant)) return false; > val other = that as Variant; > if (kind != other.kind) return false; > switch (kind) { > case 0: return c == other.c; > case 1: return i == other.i; > case 2: return f == other.f; > case 3: return o == other.o; > } > assert false : "Kind value unrecognised"; > return false; > } > > public def hashCode () { > switch (kind) { > case 0: return c.hashCode(); > case 1: return i.hashCode(); > case 2: return f.hashCode(); > case 3: return o.hashCode(); > } > assert false : "Kind value unrecognised"; > return 0; > } > > public def toString () { > switch (kind) { > case 0: return c.toString(); > case 1: return i.toString(); > case 2: return f.toString(); > case 3: return o.toString(); > } > assert false : "Kind value unrecognised"; > return ""; > } > > public static def main (args : Rail[String]) { > val map = new HashMap[Variant,Variant](); > map.put(3, 3.0f); > map.put(3.0f, "str"); > map.put("str", 3); > Console.OUT.println("map(3) == "+map(3)); > Console.OUT.println("map(3.0f) == "+map(3.0f)); > Console.OUT.println("map(\"str\") == "+map("str")); > } > } > > // vim: shiftwidth=4:tabstop=4:expandtab -- Igor Peshansky (note the spelling change!) IBM T.J. Watson Research Center XJ: No More Pain for XML's Gain (http://www.research.ibm.com/xj/) X10: Parallel Productivity and Performance (http://x10.sf.net/) |
From: Dave C. <dc...@do...> - 2009-07-06 05:47:19
|
* Vijay Saraswat (vi...@sa...) wrote: > how do you write a tagged union in X10..? This is what I had in mind: (I called it Variant instead. For a laugh. Or maybe because it's a shorter name than TaggedUnion) If o was an Object instead of a Value we would probably do .equals() instead of == Also, HashMap seems not to have an apply(k,v) method, although it does have a put(k,v) method. import x10.io.Console; import x10.util.HashMap; public class Variant { protected val kind: Int; protected var c: Char; public def this (v:Char) { this.c = v; this.kind = 0; } public static operator (v:Char) : Variant = new Variant(v); protected var i: Int; public def this (v:Int) { this.i = v; this.kind = 1; } public static operator (v:Int) : Variant = new Variant(v); protected var f: Float; public def this (v:Float) { this.f = v; this.kind = 2; } public static operator (v:Float) : Variant = new Variant(v); protected var o: Value; public def this (v:Value) { this.o = v; this.kind = 3; } public static operator (v:Value) : Variant = new Variant(v); public def equals (that:Ref) { if (this==that) return true; if (!(that instanceof Variant)) return false; val other = that as Variant; if (kind != other.kind) return false; switch (kind) { case 0: return c == other.c; case 1: return i == other.i; case 2: return f == other.f; case 3: return o == other.o; } assert false : "Kind value unrecognised"; return false; } public def hashCode () { switch (kind) { case 0: return c.hashCode(); case 1: return i.hashCode(); case 2: return f.hashCode(); case 3: return o.hashCode(); } assert false : "Kind value unrecognised"; return 0; } public def toString () { switch (kind) { case 0: return c.toString(); case 1: return i.toString(); case 2: return f.toString(); case 3: return o.toString(); } assert false : "Kind value unrecognised"; return ""; } public static def main (args : Rail[String]) { val map = new HashMap[Variant,Variant](); map.put(3, 3.0f); map.put(3.0f, "str"); map.put("str", 3); Console.OUT.println("map(3) == "+map(3)); Console.OUT.println("map(3.0f) == "+map(3.0f)); Console.OUT.println("map(\"str\") == "+map("str")); } } // vim: shiftwidth=4:tabstop=4:expandtab |
From: Vijay S. <vi...@sa...> - 2009-07-06 03:35:41
|
how do you write a tagged union in X10..? Dave Cunningham wrote: >>> So there are two choices >>> • Continue to treat int = inlined Integer. Now for v: >>> HashMap(Object, String), you cannot say v(3) = "Three". >>> >> I'd favor this. >> >> > > I don't see why there is all this effort to support this. If one is > sufficiently hellbent on needing such a hashmap, then one can just > define a class that implements a tagged union and use > HashMap[TaggedUnion,String], thus allowing > > v(3) = "Three" > v(3.0) = "Floating point three" > v("3") = "String three" > > etc > > I have actually done this myself in C++, when extending the > functionality of the Lua scripting language. It was trivial. I'll > write it in X10 if you want. > > We can even provide the TaggedUnion class as part of XRX, with support > for all the primitive types and Object. Including Object in the union > would allow the user to box any types that were not directly > implemented, e.g. String in the above example. We can even @NativeRep > the TaggedUnion and use a proper union { } to avoid wasting memory. > > The performance is probably acceptable for the sort of applications that > would want such a hashmap, and it avoids cluttering the semantics. > > The user can of course write their own tagged union if they have their > own classes and they want to use them as inlined keys alongside int etc > without boxing. > > > > |
From: Dave C. <dc...@do...> - 2009-07-06 03:01:41
|
> > So there are two choices > > • Continue to treat int = inlined Integer. Now for v: > > HashMap(Object, String), you cannot say v(3) = "Three". > > I'd favor this. > I don't see why there is all this effort to support this. If one is sufficiently hellbent on needing such a hashmap, then one can just define a class that implements a tagged union and use HashMap[TaggedUnion,String], thus allowing v(3) = "Three" v(3.0) = "Floating point three" v("3") = "String three" etc I have actually done this myself in C++, when extending the functionality of the Lua scripting language. It was trivial. I'll write it in X10 if you want. We can even provide the TaggedUnion class as part of XRX, with support for all the primitive types and Object. Including Object in the union would allow the user to box any types that were not directly implemented, e.g. String in the above example. We can even @NativeRep the TaggedUnion and use a proper union { } to avoid wasting memory. The performance is probably acceptable for the sort of applications that would want such a hashmap, and it avoids cluttering the semantics. The user can of course write their own tagged union if they have their own classes and they want to use them as inlined keys alongside int etc without boxing. |
From: Nathaniel N. <na...@na...> - 2009-07-05 11:33:26
|
On Jul 5, 2009, at 12:23 PM, Vijay Saraswat wrote: > (You should all have received mail through x10-core as well.. in > case you did not, please send me mail and tell me which userid you > need on the list.) > > The flaw in the design below is that it is not ok to permit a value > v of type inlined C to be converted to new v when upcast to Object. > For, v == w (structural equality) does not necessarily imply new v > == new w, since at type C, == means equality of reference, not > structural equality. So we will have the unacceptable situation v == > w but v to Object != w to Object. > Olivier and Nate -- you had introduced === as distinct from == > earlier. How would that help in the present circumstance? i.e. how > would you define structural equality on all classes? > == was defined as structural equality on final fields (we could define it as structural equality for properties). Thus if v==w, new v==new w, but new v !== new w. > In any case we face the same issue -- === is not available on types > inlined C. > This can be fixed by abandoning the unified type system, and > specifying not (inlined C <: Object), and not permitting == and != > to be used at values of type inlined C. Note that the Any trick > cannot work for arbitrary inlined C, since the Any trick relies on > representing a value of type inlined C within a tagged > representation of fixed size. But the size of an arbitrary inlined C > is not fixed -- it could be bigger than K words for any fixed K. Not > having a fixed-size representation for Any is a problem ... this > brings us back to disembodied objects. > We abandoned the unified type system: inlined C !<: Object. inlined C could still be a subtype of Any, but we would have to introduce autoboxing for Any rather than using the 3-word representation. > So there are two choices > • Continue to treat int = inlined Integer. Now for v: > HashMap(Object, String), you cannot say v(3) = "Three". I'd favor this. > • Treat the "built-ins" specially. Introduce Any s.t. Object <: > Any and int <: Any, uint <: Any etc -- int, uint etc -- as we were > doing before. Now a value of type Any is a tag + the represenation > of the value. The size of this value is size(tag) + max size(P), for > the primitives P, and that is size(double). Now we can have > v:HashMap(Any, String), and say v(3)="Three". We cannot say: > c:inlined Complex = ....; v(c)= c.r + "," + c.i; because inlined > Complex is not a subtype of Any. > To summarize: > • The type system looks like: Object <: Any, uint <: Any, int <: > Any etc and unrelated inlined C, for all immutable final classes C. > Therefore an inlined C value cannot be passed into a variable of > type Any. > • The user can only defined classes C below Object. From this the > type inlined C is automatically constructed, if C is a final class > that has no mutable state. > • ==, != are not available at inlined C. They are available at Any. > • If you want to use ==, != on a generic type parameter X, you will > be forced to specify X <: Any. > • Type parameters X in generic methods are required to satisfy X <: > Any. > • Since there is no implicit conversion from inlined C to C, we > will need to reintroduce some kind of closed annotation on methods > (prolly, its dual, open). Methods not marked open are available on > both inlined C and C. Methods marked open are available only on C. > They may use == and != on this. > > Vijay Saraswat wrote: >> >> The internal wiki page for this note is http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhInlinedImmutableClasses >> >> Comments please! >> >> In particular I would like to hear your thoughts on whether the >> extra complexity of the XtenTwoOhInlined design is worth the extra >> power (it can handle final classes with mutable state whereas >> XtenTwoOhInlined cannot). >> >> Best, >> Vijay >> ========================== >> • Created on July 4, 2009 >> • Last update: July 4, 2009 >> • Base link: XtenTwoOhDesign >> inlined as a type constructor >> Here is an attempt to work out Nate's idea that inlined should be a >> type constructor. The only way I can make it work without >> introducing significant complications is to have it apply only to >> final classes with no mutable state. >> >> Basic design >> inlined is a type constructor. For any final class C with no >> mutable state, inlined C is a type. (C is permitted to implement >> interfaces.) >> >> The type inlined T may be used in most places that a class type is >> used: >> >> • As the type of a local variable or loop variable or catch >> variable. >> • As the type of a method or closure parameter. >> • As the return type of a method, closure or constructor. >> • In a classcast. >> • In an instanceof. >> • In the RHS of typedef. >> • As the type of a field. >> • In the invocation of a generic method or a constructor. >> • As an actual type parameter for a generic class. >> It cannot be used >> >> • As the superclass in a class definition >> Values of this type are implemened by inlining their state in the >> enclosing container. For local variables, the container is the >> stack frame, for fields, the container is the object, for method >> parameters, the container is the parameter list (part of the stack >> frame). Values of type inlined T may also be returned from methods >> -- in this case the method may return multiple words on the stack. >> >> A non-null value v of type T may be assigned into a variable of >> type inlined T. A value of type inlined T may be assigned into a >> variable of type inlined T. In both cases the state of the RHS is >> copied into the state of the LHS. >> >> A value of type inlined T is created by calling any constructor for >> type T as a static method (i.e. not prefixing the call with new). >> >> Values of type inlined T may be compared via == with other values >> of type inlined T. This operation performs a component-wise == >> comparison. One is free to define comparison of a T value with an >> inlined T value as well in the same way. >> >> >> • Note that this implies the time taken by == on values of type >> inlined T is proportional to the size of T. >> If v is a value of type inlined T, new v is a value of type T that >> is ==. This may be a newly created object or a previously interned >> object. >> >> • This is a bit odd in that new may not necessarily construct a >> new object but nothing breaks with this semantics. >> A value v of type inlined T can be assigned to a variable x of type >> T; this is taken as shorthand for assigning new v to x. >> >> Since there is no mutable state in an inlined T value, there is no >> reason to support a ref T. >> >> Since there is no mutable state in an inlined T value, there is no >> reason to introduce a closed qualifier on methods or "=scoped=" >> types. Within the body of a final class T (with immutable fields) >> the type of this may be taken as inlined T. Therefore, any method >> on T can be called on an inlined T. Every field of T is available >> for a value v of type inlined T. >> >> >> • As described above, a value of type inlined T can be coerced to >> T when needed (auto-boxing). >> Primitive types >> The class Integer is defined as a user-defined class with >> primitively implemented state. The state of the class is a single >> field, data whose type is Data32. This is a private class defined >> in x10.lang and implemented primitively. The file int.x10 contains: >> >> package x10.lang; >> public typedef int = inlined Integer; >> >> Similarly for all the N built-in classes. >> >> • Note this is subtly different from the class Integer in Java, >> which has a field of type int. >> Type system >> The type system looks as follows. The top of the hierarchy is >> Object. Every defined class inherits from Object. Each type inlined >> C inherits from Object and is incomparable with any other type. >> >> • A value v of type inlined T can be cast to Object. This is >> implemented by boxing, i.e. creating a value of type T that is == v. >> • A downcast from a value v of type Object to inlined T succeeds >> if the runtime type of v is T. >> Generics There are no complications with generics. >> final class Pair[S,T] { >> val first: S; >> val second: T; >> def this(f: S, s: T) { >> this.first=f; >> this.second=s; >> } >> def first(): S=first; >> def second(): T=second; >> def hashCode() = first.hashCode() + second.hashCode(); >> def toString() = "<" + first.toString() + "." + second.toString() >> +">"; >> def equals(o:Object) { >> if (o instanceof Pair[S,T]) { >> val o1 = o to Pair[S,T]; >> return first.equals(o1.first) && second.equals(o2.second); >> } >> return false; >> } >> } >> >> The user may declare types Pair[Complex,Complex], >> Pair[complex,String], inlined Pair[complex, complex] etc. They >> behave as expected. >> >> Pros Simple. This is very good! >> The three use cases I had proposed earlier work with this proposal: >> >> • It should be possible to inline Complex in a Rail or ValRail. >> • It should be possible to inline the ValRail[char] field in String. >> • It should be possible to inline ValRail?[int] in Point. >> The user-defined primitive classes I had proposed can be >> implemented as normal classes C, and then inlined C can be used in >> place of the primitive type. >> >> Example >> >> // in complex.x10 >> public typedef complex = inlined Complex; >> // in Complex.x10 >> public final class Complex { >> val r:double; >> val i:double; >> Complex(r:double, i:double) { >> this.r=r; >> this.i=i; >> } >> public def neg() = Complex(-r,-i); >> public def add(c:complex) = Complex(r+d.r,i+c.i); >> public def mul(c:complex) = Complex(r*c.r - i*c.i, r*c.i+i*c.r); >> ... >> } >> >> EndOfExample. >> There is no need for special treatment of built-in primitives. This >> means that Any is not needed either. >> >> There is no need for the closed or ref qualifier. >> >> Cons >> • Classes with mutable state cannot be inlined. >> • User has to be aware of auto-boxing. >> -- VijaySaraswat - 04 Jul 2009 > |
From: Vijay S. <vi...@sa...> - 2009-07-05 10:23:21
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type"> </head> <body bgcolor="#ffffff" text="#000000"> (You should all have received mail through x10-core as well.. in case you did not, please send me mail and tell me which userid you need on the list.)<br> <br> The flaw in the design below is that it is not ok to permit a value <tt>v</tt> of type <tt>inlined C</tt> to be converted to <tt>new v</tt> when upcast to <tt>Object</tt>. For, <tt>v == w</tt> (structural equality) does not necessarily imply <tt>new v == new w</tt>, since at type <tt>C</tt>, <tt>==</tt> means equality of reference, not structural equality. So we will have the unacceptable situation v == w but v to Object != w to Object.<br> <blockquote>Olivier and Nate -- you had introduced === as distinct from == earlier. How would that help in the present circumstance? i.e. how would you define structural equality on all classes? <br> <br> In any case we face the same issue -- === is not available on types inlined C.<br> </blockquote> This can be fixed by abandoning the unified type system, and specifying <tt>not (inlined C <: Object)</tt>, and not permitting == and != to be used at values of type <tt>inlined C</tt>. Note that the <tt>Any</tt> trick cannot work for arbitrary <tt>inlined C</tt>, since the <tt>Any</tt> trick relies on representing a value of type <tt>inlined C</tt> within a tagged representation of fixed size. But the size of an arbitrary <tt>inlined C</tt> is not fixed -- it could be bigger than K words for any fixed K. Not having a fixed-size representation for Any is a problem ... this brings us back to disembodied objects. <br> <br> So there are two choices<br> <ol> <li>Continue to treat int = inlined Integer. Now for <tt>v: HashMap(Object, String)</tt>, you cannot say <tt>v(3) = "Three"</tt>.</li> <li>Treat the "built-ins" specially. Introduce Any s.t. <tt>Object <: Any</tt> and <tt>int <: Any, uint <: Any </tt>etc -- int, uint etc -- as we were doing before. Now a value of type <tt>Any</tt> is a tag + the represenation of the value. The size of this value is size(tag) + max size(P), for the primitives P, and that is size(double). Now we can have <tt>v:HashMap(Any, String)</tt>, and say <tt>v(3)="Three"</tt>. We cannot say: c<tt>:inlined Complex = ....; v(c)= c.r + "," + c.i; </tt>because <tt>inlined Complex </tt>is not a subtype of <tt>Any</tt>.<br> </li> </ol> To summarize:<br> <ol> <li>The type system looks like: <tt>Object <: Any</tt>, <tt>uint <: Any, int <: Any</tt> etc and unrelated <tt>inlined C</tt>, for all immutable final classes C. Therefore an <tt>inlined C</tt> value cannot be passed into a variable of type Any.<br> </li> <li>The user can only defined classes C below Object. From this the type <tt>inlined C</tt> is automatically constructed, if C is a final class that has no mutable state.</li> <li>==, != are not available at inlined C. They are available at Any. <br> </li> <li>If you want to use <tt>==, !=</tt> on a generic type parameter <tt>X</tt>, you will be forced to specify <tt>X <: Any</tt>.<br> </li> <li> Type parameters <tt>X</tt> in generic methods are required to satisfy <tt>X <: Any</tt>.</li> <li>Since there is no implicit conversion from <tt>inlined C</tt> to <tt>C</tt>, we will need to reintroduce some kind of <tt>closed</tt> annotation on methods (prolly, its dual, <tt>open</tt>). Methods not marked <tt>open</tt> are available on both <tt>inlined C</tt> and <tt>C</tt>. Methods marked open are available only on <tt>C</tt>. They may use <tt>==</tt> and <tt>!=</tt> on this.<br> </li> </ol> <br> Vijay Saraswat wrote: <blockquote cite="mid:4A4...@sa..." type="cite">The internal wiki page for this note is <a moz-do-not-send="true" class="moz-txt-link-freetext" href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhInlinedImmutableClasses">http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhInlinedImmutableClasses</a><br> <br> Comments please! <br> <br> In particular I would like to hear your thoughts on whether the extra complexity of the XtenTwoOhInlined design is worth the extra power (it can handle final classes with mutable state whereas XtenTwoOhInlined cannot).<br> <br> Best,<br> Vijay<br> ==========================<br> <ul> <li> Created on July 4, 2009 </li> <li> Last update: July 4, 2009 </li> <li> Base link: <a moz-do-not-send="true" href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign" class="twikiLink">XtenTwoOhDesign</a> </li> </ul> <h1><a moz-do-not-send="true" name="inlined_as_a_type_constructor"></a><a moz-do-not-send="true" name="_inlined_as_a_type_constructor"></a> <code>inlined</code> as a type constructor </h1> <p> <em>Here is an attempt to work out Nate's idea that <code>inlined</code> should be a type constructor. The only way I can make it work without introducing significant complications is to have it apply only to <code>final</code> classes with no mutable state.</em> </p> <h1><a moz-do-not-send="true" name="Basic_design"></a> Basic design </h1> <p> <code>inlined</code> is a type constructor. For any <code>final</code> class <code>C</code> with no mutable state, <code>inlined C</code> is a type. (<code>C</code> is permitted to implement interfaces.) </p> <p>The type <code>inlined T</code> may be used in most places that a class type is used: </p> <ul> <li> As the type of a local variable or loop variable or catch variable. </li> <li> As the type of a method or closure parameter. </li> <li> As the return type of a method, closure or constructor. </li> <li> In a classcast. </li> <li> In an <code>instanceof</code>. </li> <li> In the RHS of <code>typedef</code>. </li> <li> As the type of a field. </li> <li> In the invocation of a generic method or a constructor. </li> <li> As an actual type parameter for a generic class. </li> </ul> <p>It cannot be used </p> <ul> <li> As the superclass in a <code>class</code> definition </li> </ul> <p>Values of this type are implemened by inlining their state in the enclosing container. For local variables, the container is the stack frame, for fields, the container is the object, for method parameters, the container is the parameter list (part of the stack frame). Values of type <code>inlined T</code> may also be returned from methods -- in this case the method may return multiple words on the stack. </p> <p>A non-null value <code>v</code> of type <code>T</code> may be assigned into a variable of type <code>inlined T</code>. A value of type <code>inlined T</code> may be assigned into a variable of type <code>inlined T</code>. In both cases the state of the RHS is copied into the state of the LHS. </p> <p>A value of type <code>inlined T</code> is created by calling any constructor for type <code>T</code> as a static method (i.e. not prefixing the call with <code>new</code>). </p> <p>Values of type <code>inlined T</code> may be compared via <code>==</code> with other values of type <code>inlined T</code>. This operation performs a component-wise <code>==</code> comparison. One is free to define comparison of a <code>T</code> value with an <code>inlined T</code> value as well in the same way. </p> <p> </p> <ul> <li> Note that this implies the time taken by <code>==</code> on values of type <code>inlined T</code> is proportional to the size of <code>T</code>. </li> </ul> <p>If <code>v</code> is a value of type <code>inlined T</code>, <code>new v</code> is a value of type <code>T</code> that is <code>==</code>. This may be a newly created object or a previously interned object. </p> <ul> <li> This is a bit odd in that <code>new</code> may not necessarily construct a new object but nothing breaks with this semantics. </li> </ul> <p>A value <code>v</code> of type <code>inlined T</code> can be assigned to a variable <code>x</code> of type <code>T</code>; this is taken as shorthand for assigning <code>new v</code> to <code>x</code>. </p> <p>Since there is no mutable state in an <code>inlined T</code> value, there is no reason to support a <code>ref T</code>. </p> <p>Since there is no mutable state in an <code>inlined T</code> value, there is no reason to introduce a <code>closed</code> qualifier on methods or "=scoped=" types. Within the body of a <code>final</code> class <code>T</code> (with immutable fields) the type of <code>this</code> may be taken as <code>inlined T</code>. Therefore, any method on <code>T</code> can be called on an <code>inlined T</code>. Every field of <code>T</code> is available for a value <code>v</code> of type <code>inlined T</code>. </p> <p> </p> <ul> <li> As described above, a value of type <code>inlined T</code> can be coerced to <code>T</code> when needed (auto-boxing). </li> </ul> <h3><a moz-do-not-send="true" name="Primitive_types"></a> Primitive types </h3> <p> The class <code>Integer</code> is defined as a user-defined class with primitively implemented state. The state of the class is a single field, <code>data</code> whose type is <code>Data32</code>. This is a private class defined in <code>x10.lang</code> and implemented primitively. The file <code>int.x10</code> contains: </p> <pre>package x10.lang; public typedef int = inlined Integer; </pre> <p> Similarly for all the N built-in classes. </p> <ul> <li> Note this is subtly different from the class <code>Integer</code> in Java, which has a field of type <code>int</code>. </li> </ul> <h3><a moz-do-not-send="true" name="Type_system"></a> Type system </h3> <p> The type system looks as follows. The top of the hierarchy is <code>Object</code>. Every defined class inherits from <code>Object</code>. Each type <code>inlined C</code> inherits from <code>Object</code> and is incomparable with any other type. </p> <ul> <li> A value <code>v</code> of type <code>inlined T</code> can be cast to <code>Object</code>. This is implemented by boxing, i.e. creating a value of type <code>T</code> that is <code>==</code> <code>v</code>. </li> <li> A downcast from a value <code>v</code> of type <code>Object</code> to <code>inlined T</code> succeeds if the runtime type of <code>v</code> is <code>T</code>. </li> </ul> <h3><a moz-do-not-send="true" name="Generics"></a> Generics </h3> There are no complications with generics. <pre>final class Pair[S,T] { val first: S; val second: T; def this(f: S, s: T) { this.first=f; this.second=s; } def first(): S=first; def second(): T=second; def hashCode() = first.hashCode() + second.hashCode(); def toString() = "<" + first.toString() + "." + second.toString() +">"; def equals(o:Object) { if (o instanceof Pair[S,T]) { val o1 = o to Pair[S,T]; return first.equals(o1.first) && second.equals(o2.second); } return false; } } </pre> <p> The user may declare types <code>Pair[Complex,Complex]</code>, <code>Pair[complex,String]</code>, <code>inlined Pair[complex, complex]</code> etc. They behave as expected. </p> <h1><a moz-do-not-send="true" name="Pros"></a> Pros </h1> Simple. This is very good! <p>The three use cases I had proposed earlier work with this proposal: </p> <ul> <li> It should be possible to inline <code>Complex</code> in a <code>Rail</code> or <code>ValRail</code>. </li> <li> It should be possible to inline the <code>ValRail[char]</code> field in <code>String</code>. </li> <li> It should be possible to <code>inline <span class="twikiNewLink">ValRail<a moz-do-not-send="true" rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/ValRail?topicparent=Main.XtenTwoOhInlinedImmutableClasses" title="Create this topic"><sup>?</sup></a></span>[int]</code> in <code>Point</code>. </li> </ul> <p>The user-defined primitive classes I had proposed can be implemented as normal classes <code>C</code>, and then <code>inlined C</code> can be used in place of the primitive type. </p> <p><strong>Example</strong> </p> <pre>// in complex.x10 public typedef complex = inlined Complex; // in Complex.x10 public final class Complex { val r:double; val i:double; Complex(r:double, i:double) { this.r=r; this.i=i; } public def neg() = Complex(-r,-i); public def add(c:complex) = Complex(r+d.r,i+c.i); public def mul(c:complex) = Complex(r*c.r - i*c.i, r*c.i+i*c.r); ... } </pre> <em>EndOfExample.</em> <p>There is no need for special treatment of built-in primitives. This means that <code>Any</code> is not needed either. </p> <p>There is no need for the <code>closed</code> or <code>ref</code> qualifier. </p> <h1><a moz-do-not-send="true" name="Cons"></a> Cons </h1> <ul> <li> Classes with mutable state cannot be inlined. </li> <li> User has to be aware of auto-boxing. </li> </ul> -- <a moz-do-not-send="true" href="http://xj.watson.ibm.com/twiki/bin/view/Main/VijaySaraswat" class="twikiLink">VijaySaraswat</a> - 04 Jul 2009 </blockquote> <br> </body> </html> |
From: Vijay S. <vi...@sa...> - 2009-07-04 16:07:04
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> </head> <body bgcolor="#ffffff" text="#000000"> The internal wiki page for this note is <a class="moz-txt-link-freetext" href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhInlinedImmutableClasses">http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhInlinedImmutableClasses</a><br> <br> Comments please! <br> <br> In particular I would like to hear your thoughts on whether the extra complexity of the XtenTwoOhInlined design is worth the extra power (it can handle final classes with mutable state whereas XtenTwoOhInlined cannot).<br> <br> Best,<br> Vijay<br> ==========================<br> <ul> <li> Created on July 4, 2009 </li> <li> Last update: July 4, 2009 </li> <li> Base link: <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/XtenTwoOhDesign" class="twikiLink">XtenTwoOhDesign</a> </li> </ul> <p></p> <h1><a name="inlined_as_a_type_constructor"></a><a name="_inlined_as_a_type_constructor"></a> <code>inlined</code> as a type constructor </h1> <p> <em>Here is an attempt to work out Nate's idea that <code>inlined</code> should be a type constructor. The only way I can make it work without introducing significant complications is to have it apply only to <code>final</code> classes with no mutable state.</em> </p> <p></p> <h1><a name="Basic_design"></a> Basic design </h1> <p> <code>inlined</code> is a type constructor. For any <code>final</code> class <code>C</code> with no mutable state, <code>inlined C</code> is a type. (<code>C</code> is permitted to implement interfaces.) </p> <p>The type <code>inlined T</code> may be used in most places that a class type is used: </p> <ul> <li> As the type of a local variable or loop variable or catch variable. </li> <li> As the type of a method or closure parameter. </li> <li> As the return type of a method, closure or constructor. </li> <li> In a classcast. </li> <li> In an <code>instanceof</code>. </li> <li> In the RHS of <code>typedef</code>. </li> <li> As the type of a field. </li> <li> In the invocation of a generic method or a constructor. </li> <li> As an actual type parameter for a generic class. </li> </ul> <p>It cannot be used </p> <ul> <li> As the superclass in a <code>class</code> definition </li> </ul> <p>Values of this type are implemened by inlining their state in the enclosing container. For local variables, the container is the stack frame, for fields, the container is the object, for method parameters, the container is the parameter list (part of the stack frame). Values of type <code>inlined T</code> may also be returned from methods -- in this case the method may return multiple words on the stack. </p> <p>A non-null value <code>v</code> of type <code>T</code> may be assigned into a variable of type <code>inlined T</code>. A value of type <code>inlined T</code> may be assigned into a variable of type <code>inlined T</code>. In both cases the state of the RHS is copied into the state of the LHS. </p> <p>A value of type <code>inlined T</code> is created by calling any constructor for type <code>T</code> as a static method (i.e. not prefixing the call with <code>new</code>). </p> <p>Values of type <code>inlined T</code> may be compared via <code>==</code> with other values of type <code>inlined T</code>. This operation performs a component-wise <code>==</code> comparison. One is free to define comparison of a <code>T</code> value with an <code>inlined T</code> value as well in the same way. </p> <p> </p> <ul> <li> Note that this implies the time taken by <code>==</code> on values of type <code>inlined T</code> is proportional to the size of <code>T</code>. </li> </ul> <p>If <code>v</code> is a value of type <code>inlined T</code>, <code>new v</code> is a value of type <code>T</code> that is <code>==</code>. This may be a newly created object or a previously interned object. </p> <ul> <li> This is a bit odd in that <code>new</code> may not necessarily construct a new object but nothing breaks with this semantics. </li> </ul> <p>A value <code>v</code> of type <code>inlined T</code> can be assigned to a variable <code>x</code> of type <code>T</code>; this is taken as shorthand for assigning <code>new v</code> to <code>x</code>. </p> <p>Since there is no mutable state in an <code>inlined T</code> value, there is no reason to support a <code>ref T</code>. </p> <p>Since there is no mutable state in an <code>inlined T</code> value, there is no reason to introduce a <code>closed</code> qualifier on methods or "=scoped=" types. Within the body of a <code>final</code> class <code>T</code> (with immutable fields) the type of <code>this</code> may be taken as <code>inlined T</code>. Therefore, any method on <code>T</code> can be called on an <code>inlined T</code>. Every field of <code>T</code> is available for a value <code>v</code> of type <code>inlined T</code>. </p> <p> </p> <ul> <li> As described above, a value of type <code>inlined T</code> can be coerced to <code>T</code> when needed (auto-boxing). </li> </ul> <p></p> <h3><a name="Primitive_types"></a> Primitive types </h3> <p> The class <code>Integer</code> is defined as a user-defined class with primitively implemented state. The state of the class is a single field, <code>data</code> whose type is <code>Data32</code>. This is a private class defined in <code>x10.lang</code> and implemented primitively. The file <code>int.x10</code> contains: </p> <p></p> <pre>package x10.lang; public typedef int = inlined Integer; </pre> <p> Similarly for all the N built-in classes. </p> <ul> <li> Note this is subtly different from the class <code>Integer</code> in Java, which has a field of type <code>int</code>. </li> </ul> <p></p> <h3><a name="Type_system"></a> Type system </h3> <p> The type system looks as follows. The top of the hierarchy is <code>Object</code>. Every defined class inherits from <code>Object</code>. Each type <code>inlined C</code> inherits from <code>Object</code> and is incomparable with any other type. </p> <ul> <li> A value <code>v</code> of type <code>inlined T</code> can be cast to <code>Object</code>. This is implemented by boxing, i.e. creating a value of type <code>T</code> that is <code>==</code> <code>v</code>. </li> <li> A downcast from a value <code>v</code> of type <code>Object</code> to <code>inlined T</code> succeeds if the runtime type of <code>v</code> is <code>T</code>. </li> </ul> <p></p> <h3><a name="Generics"></a> Generics </h3> There are no complications with generics. <p></p> <p></p> <pre>final class Pair[S,T] { val first: S; val second: T; def this(f: S, s: T) { this.first=f; this.second=s; } def first(): S=first; def second(): T=second; def hashCode() = first.hashCode() + second.hashCode(); def toString() = "<" + first.toString() + "." + second.toString() +">"; def equals(o:Object) { if (o instanceof Pair[S,T]) { val o1 = o to Pair[S,T]; return first.equals(o1.first) && second.equals(o2.second); } return false; } } </pre> <p> The user may declare types <code>Pair[Complex,Complex]</code>, <code>Pair[complex,String]</code>, <code>inlined Pair[complex, complex]</code> etc. They behave as expected. </p> <p></p> <h1><a name="Pros"></a> Pros </h1> Simple. This is very good! <p>The three use cases I had proposed earlier work with this proposal: </p> <ul> <li> It should be possible to inline <code>Complex</code> in a <code>Rail</code> or <code>ValRail</code>. </li> <li> It should be possible to inline the <code>ValRail[char]</code> field in <code>String</code>. </li> <li> It should be possible to <code>inline <span class="twikiNewLink">ValRail<a rel="nofollow" href="http://xj.watson.ibm.com/twiki/bin/edit/Main/ValRail?topicparent=Main.XtenTwoOhInlinedImmutableClasses" title="Create this topic"><sup>?</sup></a></span>[int]</code> in <code>Point</code>. </li> </ul> <p>The user-defined primitive classes I had proposed can be implemented as normal classes <code>C</code>, and then <code>inlined C</code> can be used in place of the primitive type. </p> <p><strong>Example</strong> </p> <pre>// in complex.x10 public typedef complex = inlined Complex; // in Complex.x10 public final class Complex { val r:double; val i:double; Complex(r:double, i:double) { this.r=r; this.i=i; } public def neg() = Complex(-r,-i); public def add(c:complex) = Complex(r+d.r,i+c.i); public def mul(c:complex) = Complex(r*c.r - i*c.i, r*c.i+i*c.r); ... } </pre> <em>EndOfExample.</em> <p>There is no need for special treatment of built-in primitives. This means that <code>Any</code> is not needed either. </p> <p>There is no need for the <code>closed</code> or <code>ref</code> qualifier. </p> <p></p> <h1><a name="Cons"></a> Cons </h1> <ul> <li> Classes with mutable state cannot be inlined. </li> <li> User has to be aware of auto-boxing. </li> </ul> <p></p> -- <a href="http://xj.watson.ibm.com/twiki/bin/view/Main/VijaySaraswat" class="twikiLink">VijaySaraswat</a> - 04 Jul 2009 </body> </html> |