Re: Self-type
Timo Kinnunen
timo.kinnunen at gmail.com
Wed Jun 17 09:11:05 UTC 2015
Hi,
Maybe it could instead be made say that the receiver parameter is optional and that if it isn’t present its type is considered to be the type of the declaring class or interface, and otherwise treat its type like any other parameters’.
It seems to me that the receiver type being treated as a special case from the types of the other parameters is an unnecessary inconsistency. Other than where other special cases might prohibit it, you could rewrite methods to add a duplicate of the receiver as a method’s first parameter, then inside the method assert that both are the same and move the rest of the method implementation into a static method:
interface Set<T> extends Iterable<T> {
default <THIS extends Set<T2>, T2> THIS addAll(THIS thiz, Iterable<T2> iterable) {
assert this == thiz;
return addAll_impl(thiz, iterable);
}
static <THIS extends Set<T2>, T2> THIS addAll_impl(THIS thiz, Iterable<T2> iterable) {
THIS result = thiz;
for(T2 item : iterable) {
result = result.add(result, item);
}
return result;
}
<THIS extends Set<T2>, T2> THIS add(THIS thiz, T2 t);
}
Now the receiver is only used for method invocation and it along with its type is forgotten right after that. The type of the receiver duplicate is treated normally and things still work as expected.
Given how small role the receiver now has why not then fold it and its duplicate parameter back into one parameter that provides the features of both of them? In addition to self-types, a thusly combined receiver parameter could also provide type-specialization.
Here’s how you could have some containers provide flattening depending on what their types are. Simulated as in above to allow compiling with current compilers:
default <THIS extends Set<C>, C extends Set<T2>, T2> C flatten(THIS thiz) {
assert this == thiz;
return flatten_impl(thiz);
}
static <THIS extends Set<C>, C extends Set<T2>, T2> C flatten_impl(THIS thiz) {
C result = null;
for(C items : thiz) {
result = result == null ? items : result.addAll(result, items);
}
return result;
}
This works as expected:
List<Set<String>> sets = //…
Set<String> set = sets.flatten(sets);
Set<List<String>> lists = //…
List<String> list = lists.flatten(lists);
Or you could provide joining for a container of Strings:
interface List<T> extends Set<T> {
default <THIS extends List<CS>, CS extends CharSequence> String join(THIS thiz, CharSequence delim) {
assert this == thiz;
return join_impl(thiz, delim);
}
static <THIS extends List<CS>, CS extends CharSequence> String join_impl(THIS thiz, CharSequence delim) {
return String.join(delim, thiz);
}
}
Usable as expected:
List<List<String>> linesOfFiles = //…
List<String> lines = linesOfFiles.flatten(linesOfFiles);
String file = lines.join(lines, "\n");
This form of type specialization is somewhat similar to what is being explored in Project Valhalla but I don’t think Valhalla is looking into fixing these kinds of receiver type inconsistencies.
--
Have a nice day,
Timo.
Sent from Windows Mail
From: Alex Buckley
Sent: Wednesday, June 17, 2015 0:53
To: Timo Kinnunen, jdk9-dev at openjdk.java.net
Because the "B this" term in your method signature is a receiver
parameter. Per JLS8 8.4.1, a receiver parameter "represents the object
for which the method is invoked" and "exists solely to allow the type of
the represented object to be denoted in source code, so that the type
may be annotated." The only type permitted for a receiver parameter is
the enclosing class, i.e., a self type but not with the generality you
desire.
Alex
On 6/16/2015 2:23 AM, Timo Kinnunen wrote:
> How come this simple and obvious thing doesn’t work?
>
>
> <B> B init(B this, T value) {
>
> this.item = value;
>
> return this;
>
> }
>
>
> To me that says: “whatever type you used as a receiver, you get the same back”, as simple as that. The equivalent static method version works as expected but isn’t as convenient to use for consumers:
>
>
> static <B extends Box<T>, T> B init2_OK(B thiz, T value) {
>
> thiz.item = value;
>
> return thiz;
>
> }
>
>
> This similar construct works as well but requires extra discipline from the consumer:
>
>
> <B> B init3_OK(B thiz, T value) {
>
> this.item = value;
>
> return thiz;
>
> }
>
>
> This is the closest I have gotten to a workaround, but it is obviously not completely type-safe:
>
>
> <B extends Box<T>> B init4_OKISH(T value) {
>
> this.item = value;
>
> return thiz();
>
> }
>
> private <B extends Box<T>> B thiz() {
>
> @SuppressWarnings("unchecked")
>
> B thiz = (B) this;
>
> return thiz;
>
> }
>
>
> A little help from the language, please?
>
>
>
>
>
More information about the jdk9-dev
mailing list