manifold-systems / manifold

Manifold is a Java compiler plugin, its features include Metaprogramming, Properties, Extension Methods, Operator Overloading, Templates, a Preprocessor, and more.
http://manifold.systems/
Apache License 2.0
2.43k stars 125 forks source link

Provide `auto` type inference for local vars, fields, and method return types #371

Closed rsmckinney closed 2 years ago

rsmckinney commented 2 years ago

Proposal

Provide auto functionality for local vars, fields, and method return types.

Rationale: Although Java 10+ provides var to infer the type of local variables, it can't be applied to fields and methods. Additionally, var is not available in Java 8, which is where much Java dev still happens, particularly with Android. Thus, the proposed auto functionality fills in these gaps while not interfering with existing functionality.

Local variable example

public void post(String stuff) {
  auto list = new ArrayList<>();
  list.add(stuff);
  post(list);
}

Auto field and method example

public class Employee {
  private auto contacts = new ConcurrentHashMap<String, Contact<Person>>();
...
  public auto findContact(String key) {
    return contact.get(key);
  }
}

In some cases it is vital to infer the return type of a method so that a generated type may be conveyed to the call-site. One such case involves separate feature requests for type-safe multiple return values and, more generally, tuple expressions.

Tuples example

auto record = (material: "Basalt", weight: "5296.8", departs: LocalDate.from(2022, 5, 6), origin: "Mexico");
auto date = record.departs;

Multiple return values

public auto getBoundaries() {
  int min = findMin();
  int max = findMax();
  return min, max;
}
...
auto bounds = getBoundaries();
int min = bounds.min;
int max = bounds.max;

Limitations

There are a few limitations, at least in the first draft of the feature.

LUB method return type

Used as a method return type, auto poses a challenge in terms of reflecting a complete type. Return type inference must take into account methods having multiple return statements where return expressions may have different, but related types. Specifically, a "least upper bound" (LUB) algorithm must be applied to properly capture the type. Such a type may result in an intersection type reflecting all the common interfaces between the varying return expression types. Although the Java compiler provides limited support for such types, the JVM does not; intersection types are not supported in method signatures. As a compromise the algorithm will use heuristics in an attempt to infer the most relevant type from the intersection of types e.g., CharSequence wins over Serializable.

Head recursion

An auto return type supports tail recursion, but not head recursion. This is because return type analysis visits method call sites in a top-down fashion. If a recursive auto call precedes the first non-recursive return statement (head recursion), the method's type can't be inferred. Note, this is a first-draft limitation that will likely be remedied in a future revision.

Concerns

Careless use of auto with non-private fields and methods can lead to an overexposed API. For instance, exposing an ArrayList<String> as opposed to List<String> may be an unintentional consequence of using auto. However, considering the bulk of fields in most applications are private, perhaps having auto vs. not is a reasonable trade-off. Similarly, method return type inference via auto should be used judiciously for public APIs.

rsmckinney commented 2 years ago

See https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-ext#type-inference-with-auto

Feature available with release 2022.1.16