Proposal: Simplified syntax for dealing with parameterized types.

james lowden jl0235 at yahoo.com
Mon Mar 23 13:38:34 PDT 2009


Yeah, the "Map<String, String> map = new HashMap<>();" is a lot simpler (and something I've wanted. . .); I'd actually like to see both that and the ideas I've suggested implemented. . .

I may be attempting to kill multiple birds with one stone here; part of it is removing repitition from code, which the previously-floated proposal addresses.  The other thing I'm trying to do is provide an "easier" way to use parameterized types in a type-safe fashion, without (unfortunately, in my mind) providing some kind of reification.

I'm thinking of two different kinds of "type safety".  One is making it harder to "break" the parameterized type (by, for example, setting a HashMap<String,String> reference to a plain HashMap); the subclassing provides this.

The other feature I'm looking for is some kind of differentiation-of-intent; Java already has this in the case of enums (i.e., any given four-value enum is in some sense "equivalent" to any other, as they are both internally represented by one of four integer values, but you can't interconvert); I think something similar would be useful in cases like Collections (every List<String> is not necessarily intended to be the same thing); the syntactic sugar for subclassing would allow this.


--- On Mon, 3/23/09, Reinier Zwitserloot <reinier at zwitserloot.com> wrote:

> From: Reinier Zwitserloot <reinier at zwitserloot.com>
> Subject: Re: Proposal: Simplified syntax for dealing with parameterized types.
> To: jl0235 at yahoo.com
> Cc: coin-dev at openjdk.java.net
> Date: Monday, March 23, 2009, 3:16 PM
> There are many more annoying issues with subtypes, most
> notably regarding behaviour of .equals(), and the the way
> one person's MapStringString isn't compatible with
> another's MapStringString, eventhough they are both
> really meant as just 'Map<String, String>'.
> 
> Your solution to this problem seems extremely
> overengineered, while the end-result is a patchy hack at
> best.
> 
> Just allowing Map<String, String> map = new
> HashMap<>(); - an idea that's been floated many
> times before, is something I'd strongly prefer, and
> it's much simpler.
> 
>  --Reinier Zwitserloot
> 
> 
> 
> On Mar 23, 2009, at 20:40, james lowden wrote:
> 
> > 
> > Proposal: Simplified syntax for dealing with
> parameterized types.
> > 
> > AUTHOR(S):
> > 
> > J. Lowden
> > 
> > VERSION:
> > 
> > 1.0    Initial version.
> > 
> > 
> > OVERVIEW
> > 
> > FEATURE SUMMARY:
> > 
> > Declaring and instantiating parameterized types can be
> verbose (resulting in expressions like "HashMap
> <String, String>" being repeated throughtout
> one's code) and, due to the erasure-based implementation
> of generics in Java, not necessarily type-safe.  The code
> snippet below illustrates both:
> > 
> > 
> >  HashMap <String, String> a = new HashMap
> <String, String> ();
> >  HashMap b = new HashMap ();
> >  b.put ("foo", new JFrame ());
> >  a = b; // this is semantically wrong, but compiles
> > 
> > 
> > In this case, the parameterization language
> ("<String, String>") is repeated twice,
> which quickly becomes annoying if one is using a large
> number of String-to-String hashes.  More dangerously,
> despite "a" having been declared as a HashMap
> <String, String>, it is possible to use it as a
> reference to a non-parameterized HashMap which may contain
> objects of unexpected and semantically incorrect types.  One
> way around this problem is by subclassing the parameterized
> type, as in:
> > 
> > 
> >  public class StringyMap extends HashMap <String,
> String> {
> >  }
> > 
> > 
> > The above example then becomes:
> > 
> > 
> >  StringyMap a = new StringyMap ();
> >  HashMap b = new HashMap ();
> >  b.put ("foo", new JFrame ());
> >  a = b; // this now causes a compiler error
> > 
> > 
> > This both removes the repetition and causes the
> type-unsafe assignments statement to generate a compiler
> error.  However, since simply extending a class causes the
> loss of all but the default constructor, in order to make
> StringyMap as useful as HashMap <String, String>, we
> really need to do this:
> > 
> > 
> >  public class StringyMap extends HashMap <String,
> String> {
> > 
> >    public StringyMap () {
> >    }
> > 
> >    public StringyMap (int initialCapacity) {
> >      super (initialCapacity);
> >    }
> > 
> >    public StringyMap (int initialCapacity, float
> loadFactor) {
> >      super (initialCapacity, loadFactor);
> >    }
> > 
> >    public StringyMap (Map <String, String> m) {
> >      super (m);
> >    }
> > 
> >  }
> > 
> > 
> > This is an annoying heap of boilerplate code,
> especially seeing that verbosity was one of the issues we
> were trying to address.  Therefore, the new syntax being
> proposed is something along the lines of:
> > 
> >  public class X = Y <list of type parameters>;
> > 
> > which would be exactly equivalent to:
> > 
> >  public class X extends Y <list of type
> parameters> {
> > 
> >    // constructors
> > 
> > }
> > 
> > with constructors matching the signatures of the
> superclass constructors automatically generated that simply
> pass all the parameters along to the superclass constructor.
> > 
> > This could also be done with interfaces.  Since
> interfaces do not support constructors, the mapping is
> simpler, as we simply leave the automatic constructor
> creation out:
> > 
> >  public interface X = Y <list of type
> parameters>;
> > 
> > is equivalent to:
> > 
> >  public interface X extends Y <list of type
> parameters> {}
> > 
> > 
> > Java currently requires each top-level public class or
> interface to be declared in its own source file.  When
> working with multiple parameterized types, this could result
> in a large number of one-line source files that may be
> inconvenient to deal with.  Therefore, I also propose the
> creation of an optional special source file in each package
> that can be used to contain multiple such type definitions. 
> This file would be named something like
> "param-types.java" (the specific name doesn't
> matter as long as it is A) standardized and B) not a legal
> Java identifier, so we avoid collisions), and all public
> classes or interfaces defined in it would be compiled as
> though they were top-level class files in the given package.
>  The "param-types.java" file would be restricted
> to containing classes/interfaces defined using the new
> syntax described above; this file is intended as a
> convenience mechanism to support the features described
> above, not a mechanism for
> > cramming all of one's code into one hideously-long
> source file.
> > 
> > Although not a principal goal of this proposal, this
> also provides an easy mechanism for enforcing type safety
> between two types that have the same parameters but are not
> intended to be interconvertible.  For example:
> > 
> > public interface ListOfThingsToNameTheBaby = List
> <String>;
> > public interface ListOfAnnoyingInternetAcronyms = List
> <String>;
> > 
> > ListOfThingsToNameTheBaby a;
> > ListOfAnnoyingInternetAcronyms b;
> > 
> > a = b; // raises a compiler error, since these are not
> assignable
> > 
> > 
> > Note that there is potential for confusion if the two
> types were actually meant to be interconverible (i.e.,
> ListOfNamesForBabies and ListOfThingsToNameTheBaby).
> > 
> > 
> > MAJOR ADVANTAGE:
> > 
> > It becomes easier and more elegant to create reusable,
> robust parameterized types.
> > 
> > 
> > MAJOR BENEFIT:
> > 
> > Readability of code that makes extensive use of
> parameterized types is improved.  Although the general
> erasure-related issues with type-safety of generics are not
> eliminated, this provides a straightforward mechanism for
> working with more robust parameterized types.
> > 
> > 
> > MAJOR DISADVANTAGE:
> > 
> > Programmers (especially those working on distributed
> projects) may inadvertantly end up defining two types meant
> to be "the same thing" that are not mutually
> assignable, as in:
> > 
> > public class ListOfNamesForBabies = ArrayList
> <String>;
> > public class ListOfThingsToNameTheBaby = ArrayList
> <String>;
> > 
> > ListOfNamesForBabies a;
> > ListOfThingsToNameTheBaby b;
> > 
> > a = b; // does not compile
> > 
> > 
> > Note that this is only a problem in cases where the
> types are intended to be equivalent; see the
> ListOfAnnoyingInternetAcronyms/ListOfThingsToNameTheBaby
> example above.
> > 
> > 
> > ALTERNATIVES:
> > 
> > Write strings the old way by using concatenations or
> using builders.
> > 
> > 
> > EXAMPLES
> > 
> > SIMPLE EXAMPLE:
> > 
> > --- creating a file named StringyMap.java in package
> blah
> > 
> >  package blah;
> > 
> >  // various import statements
> > 
> >  public class StringyMap = HashMap <String,
> String>;
> > 
> > 
> > --- is equivalent to what would currently result from
> the following:
> > 
> >  package blah;
> > 
> >  // various import statements
> > 
> >  public class StringyMap extends HashMap <String,
> String> {
> > 
> >    public StringyMap () {
> >    }
> > 
> >    public StringyMap (int initialCapacity) {
> >      super (initialCapacity);
> >    }
> > 
> >    public StringyMap (int initialCapacity, float
> loadFactor) {
> >      super (initialCapacity, loadFactor);
> >    }
> > 
> >    public StringyMap (Map <String, String> m) {
> >      super (m);
> >    }
> > 
> >  }
> > 
> > ADVANCED EXAMPLE:
> > 
> > --- creating a file named param-types.java in package
> blah
> > 
> >  package blah;
> > 
> >  // various import statements
> > 
> >  public class StringyMap = HashMap <String,
> String>;
> >  public interface ListOfNames = List <String>;
> > 
> > --- is equivalent to what would currently result from
> defining the StringyMap class in the previous example
> > --- as well as the following:
> > 
> >  package blah;
> > 
> >  // various import statements
> > 
> >  public interface ListOfNames extends List
> <String> {}
> > 
> > 
> > DETAILS
> > 
> > SPECIFICATION:
> > 
> > The new syntax can be handled entirely via compiler
> modifications.  Since this proposal consists entirely of
> syntactic sugar atop the existing type system and uses a
> syntax that currently has no meaning in Java, it should not
> have any affect on the meaning of any current feature of the
> Java Programming Language.
> > 
> > 
> > COMPILATION:
> > 
> > Compilation of this new feature would consist of
> de-sugaring two new types of declarations.  For interfaces,
> the following:
> > 
> > [modifier_list] interface [name] = [superinterface]
> <list_of_types. . .>;
> > 
> > is de-sugared to:
> > 
> > [modifier_list] interface [name] extends
> [superinterface] <list_of_types. . .> {}
> > 
> > 
> > Classes are a bit more complex:
> > 
> > [modifier_list] class [name] = [superclass]
> <list_of_types. . .>;
> > 
> > is de-sugared to:
> > 
> > [modifier_list] class [name] extends [superclass]
> <list_of_types. . .> {
> > 
> >  [constructors]
> > 
> > }
> > 
> > Where the [constructors] block consists of one
> "wrapped" constructor for each public or protected
> constructor in the superclass.  Thus, for each such
> constructor in the superclass:
> > 
> >  [public/protected] [superclass] ([constructor_args. .
> .]);
> > 
> > A "wrapper" constructor would be generated
> as follows:
> > 
> >  [public/protected] [name] ([constructor_args. . .]) {
> >    super ([constructor_args. . .]);
> >  }
> > 
> > 
> > Finally, when resolving dependencies, if the compiler
> doesn't find a class or source file for a top-level
> class in the expected location, the compiler would examine
> "param-types.java" in the package-appropriate
> directory to see if it is defined there.  Only classes and
> interfaces defined using the syntax described here are
> permitted in the "param-types.java"; any
> "traditional" class or interface definition in
> this file results in a compiler error.
> > 
> > 
> > TESTING:
> > 
> > This feature can be tested in the same manner as any
> other form of class or interface declaration.
> > 
> > LIBRARY SUPPORT:
> > 
> > None required.
> > 
> > REFLECTIVE APIS:
> > 
> > This should be automatic, as the current reflective
> APIs will correctly identify the class or interface.
> > 
> > OTHER CHANGES:
> > 
> > None.
> > 
> > MIGRATION:
> > 
> > Replace current use of parameterized types where
> feasible.
> > 
> > 
> > COMPATIBILITY
> > 
> > BREAKING CHANGES:
> > 
> > None. The proposed syntax currently causes compiler
> errors.
> > 
> > EXISTING PROGRAMS:
> > 
> > No changes.
> > 
> > 
> > REFERENCES
> > 
> > EXISTING BUGS:
> > 
> > None that I am aware of.
> > 
> > URL FOR PROTOTYPE (optional):
> > 
> > None thus far; work-in-progress.
> > 
> > 
> > 
> >


      



More information about the coin-dev mailing list