Does anyone have context on jdk.httpserver?

2024-01-21 Thread Ethan McCue
Hi all,

Elliot[1] and I have been digging into the HTTP(S) server implementation
provided by the jdk.httpserver module.

It hasn't taken long to notice that the provided implementation
is...lacking. Both in performance, as it places extremely low and errors
out on benchmarks [2][3], and in polish, as doing relatively simple tasks
like printing out the result of HttpExchange#getRequestHeaders() does not
actually include the headers in the output.

But, the actual API isn't wretched. The overall design (with a Filter chain
and a mutable "out" object for responses) is reminiscent of Servlets and
there is an SPI hook to provide custom server implementations. And unlike
the jwebserver tool, which is built on this, there isn't a "for education
purposes only" sign anywhere and it is part of an exported and supposedly
supported module.

All that is to ask - does anyone on this mailing list have any historical
context on this? Why was it added to the JDK, what were the goals of its
implementation, are there any records of the decision process behind its
design?

Separately is there any appetite for improving the performance of the
built-in server implementation?


[1]: https://github.com/ebarlas/microhttp
[2]:
https://www.techempower.com/benchmarks/#hw=ph=plaintext=data-r22
[3]:
https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Java/httpserver


Should we actually make Connection a TemplateProcessor?

2023-09-15 Thread Ethan McCue
One of the examples in the String Template JEPs, and a stated motivating
factor behind its design, is SQL. Template processors are objects so that
use cases like constructing SQL statements aren't injection prone.

The questions I want to pose are:

* Should java.sql provide an implementation of TemplateProcessor
* Where should that implementation live?
* What, exactly, should be the translation strategy.

The reason I think this isn't an obvious yes and ask that last question is
this.

Say this is some user's code.

try (var conn = ds.getConnection();
 var stmt = conn.prepareStatement("""
 SELECT user.name
 WHERE user.height > ? AND user.width < ?
 """)) {
stmt.setInt(1, height);
stmt.setInt(2, width);
var rs = stmt.executeQuery();
process(rs);
}

The transliteration to string templates would be something like

try (var conn = ds.getConnection();
 var stmt = conn."""
 SELECT user.name
 WHERE user.height > \{height} AND user.width < \{width}
 """)) {
var rs = stmt.executeQuery();
process(rs);
}

Whether Connection implements TemplateProcessor directly or its something
that you wrap a connection with is somewhat immaterial.

How should we handle "partial templating"?

try (var conn = ds.getConnection();
 var stmt = conn."""
 SELECT user.name
 WHERE user.height > ? AND user.width < \{width}
 """)) {
var rs = stmt.executeQuery();
rs.setInt(1, height);
process(rs);
}

Or

try (var conn = ds.getConnection();
 var stmt = conn."""
 SELECT user.name
 WHERE user.height > \{height} AND user.width < ?
 """)) {
var rs = stmt.executeQuery();
rs.setInt(2, width);
process(rs);
}

Is replacing every substitution with ? and calling set* is enough? How
could it be known, without parsing the specific sql dialect, what index to
use for the parameter?

try (var conn = ds.getConnection();
 var stmt = conn."""
 SELECT user.name
 WHERE user.name <> '???'
 AND user.height > ?
 AND user.width < \{width}
 """)) {
var rs = stmt.executeQuery();
rs.setInt(1, height);
process(rs);
}

(this seems more library design than language design, hence I sent it here.
I can forward to amber-dev if that is better)


Re: The introduction of Sequenced collections is not a source compatible change

2023-05-17 Thread Ethan McCue
r own collections (or type system around collections in case
> of Kotlin), so knowing the real impact of this change is hard here.
>
> The problem of using a corpus experiment is that the corpus may not
> represent the current state of the Java ecosystem, or at least the one that
> may be impacted.
>
> The problem with the corpus experiment is also that you need to be aware
> that most moden open source projects use "--release" flag, so you have to
> patch it away from the build system.
>
> In my case, on my own repositories (public and private), i had only one
> occurrence of the issue in the main source codes because many of those
> repositories are not using 'var' or even the stream API but on the corpus
> of the unit tests we give to students to check their implementations,
> little less than a third of those JUnit classes had source compatibility
> issues because those tests are using 'var' and different collections
> heavily.
>
> And the situation is a little worst than that because in between now and
> the time people will use Java 21, a lot of codes will be written using Java
> 11 and 17 and may found incompatible later.
>
> A source incompatibility issue is not a big deal, as said in this thread,
> most of the time, explicitly fixing the type argument instead of inferring
> it make the code compile again.
> So the house is not burning, but we should raise awareness of this issue
> given that it may have a bigger impact than other source incompatible
> changes that occur previously.
>
> Rémi
>
> --
>
> *From: *"joe darcy"  
> *To: *"Ethan McCue"  , "Raffaello
> Giulietti" 
> 
> *Cc: *"Remi Forax"  , "Stuart
> Marks"  ,
> "core-libs-dev" 
> 
> *Sent: *Friday, May 5, 2023 4:38:16 AM
> *Subject: *Re: The introduction of Sequenced collections is not a source
> compatible change
>
> A few comments on the general compatibility policy for the JDK.
> Compatibility is looked after by the Compatibility and Specification Review
> (CSR) process ( Compatibility & Specification Review). Summarizing the
> approach,
>
> The general compatibility policy for exported APIs implemented in the JDK
> is:
>
> * Don't break binary compatibility (as defined in the Java Language
> Specification) without sufficient cause.
> * Avoid introducing source incompatibilities.
> * Manage behavioral compatibility changes.
>
> https://wiki.openjdk.org/display/csr/Main
>
> None of binary, source, and behavioral compatibly are absolutes and
> judgement is used to assess the cost/benefits of changes. For example,
> strict source compatibility would preclude, say, introducing new public
> types in the java.lang package since the implicit import of types in
> java.lang could conflict with a same-named type *-imported from another
> package.
>
> When a proposed change is estimated to be sufficiently disruptive, we
> conduct a corpus experiment to evaluate the impact on the change on many
> public Java libraries. Back in Project Coin in JDK 7, that basic approach
> was used to help quantify various language design choices and the
> infrastructure to run such experiments has been built-out in the subsequent
> releases.
>
> HTH,
>
> -Joe
> CSR Group Lead
> On 5/4/2023 6:32 AM, Ethan McCue wrote:
>
> I guess this a good time to ask, ignoring the benefit part of a cost
> benefit analysis, what mechanisms do we have to measure the number of
> codebases relying on type inference this will break?
>
> Iirc Adoptium built/ran the unit tests of a bunch of public repos, but
> it's also a bit shocking if the jtreg suite had nothing for this.
>
> On Thu, May 4, 2023, 9:27 AM Raffaello Giulietti <
> raffaello.giulie...@oracle.com> wrote:
>
>> Without changing the semantics at all, you could also write
>>
>> final List> list =
>> Stream.>of(nestedDequeue, nestedList).toList();
>>
>> to "help" type inference.
>>
>>
>>
>>
>> On 2023-05-03 15:12, fo...@univ-mlv.fr wrote:
>> > Another example sent to me by a fellow French guy,
>> >
>> >  final Deque nestedDequeue = new ArrayDeque<>();
>> >  nestedDequeue.addFirst("C");
>> >  nestedDequeue.addFirst("B");
>> >  nestedDequeue.addFirst("A");
>> >
>> >  final List nestedList = new ArrayList<>();
>> >  nestedList.add("D");
>> >  nestedList.add("E");
>> >  nestedList.add("F");
>> >
>> >  final List> list = Stream.of(nestedD

Re: The introduction of Sequenced collections is not a source compatible change

2023-05-04 Thread Ethan McCue
For those who are tracking but didn't click through to find it, here was
the writeup of the compatibility analysis for sequenced collections as of
9/20/22.

https://bugs.openjdk.org/secure/attachment/100811/compatibility-20220920.html

Bug page:

https://bugs.openjdk.org/browse/JDK-8266572




On Fri, May 5, 2023 at 12:08 AM Joseph D. Darcy 
wrote:

> PS And as a general policy, over the releases we've done more to
> preserve source compatibility when possible for language changes.
>
> For example, there were many source compatibility breakages when
> "assert" was added as a keyword back in JDK 1.4; the current keyword
> management policies (https://openjdk.org/jeps/8223002), including
> contextual keywords (JLS §3.9) and hyphenated-keywords, mitigate the
> impact of analogous changes today.
>
> -Joe
>
> On 5/4/2023 7:47 PM, - wrote:
> > In addition, in the CSR of sequenced collection, it already
> > anticipated some other form of source incompatibility:
> > If a class implements List and Deque at the same time, the return type
> > of reversed() must extend both interfaces as well.
> >
> > This alone would be a greater source incompatibility than this type
> > interference already; however, both are binarily compatible:
> > Java doesn't care about generics at runtime, and for default
> > reversed() overrides, reversed ()Ljava/util/List; and reversed
> > ()Ljava/util/Deque; are distinct methods; code calling reversed via
> > List or Deque interfaces will get the reversed version of respective
> > interfaces, which are functionally correct as well.
> >
> > I personally would consider this type inference usage moot. It is same
> > as adding another method addAll(List) when there is already
> > addAll(String[]): existing addAll(null) calls will break, but this is
> > not a valid argument against adding a List variant of addAll.
> >
> > On Thu, May 4, 2023 at 9:38 PM Joseph D. Darcy 
> wrote:
> >> A few comments on the general compatibility policy for the JDK.
> Compatibility is looked after by the Compatibility and Specification Review
> (CSR) process ( Compatibility & Specification Review). Summarizing the
> approach,
> >>
> >> The general compatibility policy for exported APIs implemented in the
> JDK is:
> >>
> >>  * Don't break binary compatibility (as defined in the Java
> Language Specification) without sufficient cause.
> >>  * Avoid introducing source incompatibilities.
> >>  * Manage behavioral compatibility changes.
> >>
> >> https://wiki.openjdk.org/display/csr/Main
> >>
> >> None of binary, source, and behavioral compatibly are absolutes and
> judgement is used to assess the cost/benefits of changes. For example,
> strict source compatibility would preclude, say, introducing new public
> types in the java.lang package since the implicit import of types in
> java.lang could conflict with a same-named type *-imported from another
> package.
> >>
> >> When a proposed change is estimated to be sufficiently disruptive, we
> conduct a corpus experiment to evaluate the impact on the change on many
> public Java libraries. Back in Project Coin in JDK 7, that basic approach
> was used to help quantify various language design choices and the
> infrastructure to run such experiments has been built-out in the subsequent
> releases.
> >>
> >> HTH,
> >>
> >> -Joe
> >> CSR Group Lead
> >>
> >> On 5/4/2023 6:32 AM, Ethan McCue wrote:
> >>
> >> I guess this a good time to ask, ignoring the benefit part of a cost
> benefit analysis, what mechanisms do we have to measure the number of
> codebases relying on type inference this will break?
> >>
> >> Iirc Adoptium built/ran the unit tests of a bunch of public repos, but
> it's also a bit shocking if the jtreg suite had nothing for this.
> >>
> >> On Thu, May 4, 2023, 9:27 AM Raffaello Giulietti <
> raffaello.giulie...@oracle.com> wrote:
> >>> Without changing the semantics at all, you could also write
> >>>
> >>>  final List> list =
> >>> Stream.>of(nestedDequeue, nestedList).toList();
> >>>
> >>> to "help" type inference.
> >>>
> >>>
> >>>
> >>>
> >>> On 2023-05-03 15:12, fo...@univ-mlv.fr wrote:
> >>>> Another example sent to me by a fellow French guy,
> >>>>
> >>>>   final Deque nestedDequeue = new ArrayDeque<>();
> >>>>   nestedDequeue.addFirst("C");
> >&

Re: The introduction of Sequenced collections is not a source compatible change

2023-05-04 Thread Ethan McCue
I guess this a good time to ask, ignoring the benefit part of a cost
benefit analysis, what mechanisms do we have to measure the number of
codebases relying on type inference this will break?

Iirc Adoptium built/ran the unit tests of a bunch of public repos, but it's
also a bit shocking if the jtreg suite had nothing for this.

On Thu, May 4, 2023, 9:27 AM Raffaello Giulietti <
raffaello.giulie...@oracle.com> wrote:

> Without changing the semantics at all, you could also write
>
> final List> list =
> Stream.>of(nestedDequeue, nestedList).toList();
>
> to "help" type inference.
>
>
>
>
> On 2023-05-03 15:12, fo...@univ-mlv.fr wrote:
> > Another example sent to me by a fellow French guy,
> >
> >  final Deque nestedDequeue = new ArrayDeque<>();
> >  nestedDequeue.addFirst("C");
> >  nestedDequeue.addFirst("B");
> >  nestedDequeue.addFirst("A");
> >
> >  final List nestedList = new ArrayList<>();
> >  nestedList.add("D");
> >  nestedList.add("E");
> >  nestedList.add("F");
> >
> >  final List> list = Stream.of(nestedDequeue,
> nestedList).toList();
> >
> > This one is cool because no 'var' is involved and using
> collect(Collectors.toList()) instead of toList() solves the inference
> problem.
> >
> > Rémi
> >
> > - Original Message -
> >> From: "Stuart Marks" 
> >> To: "Remi Forax" 
> >> Cc: "core-libs-dev" 
> >> Sent: Tuesday, May 2, 2023 2:44:28 AM
> >> Subject: Re: The introduction of Sequenced collections is not a source
> compatible change
> >
> >> Hi Rémi,
> >>
> >> Thanks for trying out the latest build!
> >>
> >> I'll make sure this gets mentioned in the release note for Sequenced
> >> Collections.
> >> We'll also raise this issue when we talk about this feature in the
> Quality
> >> Outreach
> >> program.
> >>
> >> s'marks
> >>
> >> On 4/29/23 3:46 AM, Remi Forax wrote:
> >>> I've several repositories that now fails to compile with the latest
> jdk21, which
> >>> introduces sequence collections.
> >>>
> >>> The introduction of a common supertype to existing collections is
> *not* a source
> >>> compatible change because of type inference.
> >>>
> >>> Here is a simplified example:
> >>>
> >>> public static void m(List>>
> factories) {
> >>> }
> >>>
> >>> public static void main(String[] args) {
> >>>   Supplier> supplier1 =
> LinkedHashMap::new;
> >>>   Supplier> supplier2 = TreeMap::new;
> >>>   var factories = List.of(supplier1, supplier2);
> >>>   m(factories);
> >>> }
> >>>
> >>>
> >>> This example compiles fine with Java 20 but report an error with Java
> 21:
> >>> SequencedCollectionBug.java:28: error: method m in class
> SequencedCollectionBug
> >>> cannot be applied to given types;
> >>>   m(factories);
> >>>   ^
> >>> required: List>>
> >>> found:List>>
> >>> reason: argument mismatch; List SequencedMap>>
> >>> cannot be converted to List>>
> >>>
> >>>
> >>>
> >>> Apart from the example above, most of the failures I see are in the
> unit tests
> >>> provided to the students, because we are using a lot of 'var' in them
> so they
> >>> work whatever the name of the types chosen by the students.
> >>>
> >>> Discussing with a colleague, we also believe that this bug is not
> limited to
> >>> Java, existing Kotlin codes will also fail to compile due to this bug.
> >>>
> >>> Regards,
> >>> Rémi
>


Re: JEP-198 - Lets start talking about JSON

2023-02-28 Thread Ethan McCue
Link to the proxy which I forgot to include

https://gist.github.com/bowbahdoe/eb29d172351162408eab5e4ee9d84fec

On Tue, Feb 28, 2023 at 12:16 PM Ethan McCue  wrote:

> As an update to my character arc, I documented and wrote up an explanation
> for the prototype library I was working on.[1]
>
> And I've gotten a good deal of feedback on reddit[2] and in private.
>
> I think its relevant to the conversation here in the sense of
>
> - There are more of rzwitserloot's objections to read on the general
> concept JSON as a built in.[3]
> - There are a lot of well reasoned objections to the manner in which I am
> interpreting a JSON tree, as well
> as objections to the usage of a tree as the core. JEP 198's current
> writeup (which I know is subject to a rewrite/retraction)
> presumes that an immutable tree would be the core data structure.
> - The peanut gallery might be interested in a "base" to implement whatever
> their take on an API should be.
>
> For that last category, I have a method-handle proxy written up for those
> who want to try the "push parser into a pull parser"
> transformation I alluded to in my first email of this thread.
>
> [1]: https://mccue.dev/pages/2-26-23-json
> [2]:
> https://www.reddit.com/r/java/comments/11cyoh1/please_try_my_json_library/
> [3]: Including one that reddit took down, but can be seen through reveddit
> https://www.reveddit.com/y/rzwitserloot/?after=t1_jacpsj6=1=new=t1_jaa3x0q_status=all
>
> On Fri, Dec 16, 2022 at 6:23 PM Ethan McCue  wrote:
>
>> Sidenote about "Project Galahad" - I know Graal uses json for a few
>> things including a reflection-config.json. Food for thought.
>>
>> > the java.util.log experiment shows that trying to ‘core-librarize’
>> needs that the community at large already fulfills with third party deps
>> isn’t a good move,
>>
>> I, personally, do not have much historical context for java.util.log.
>> What feels distinct about providing a JSON api is that
>> logging is an implicitly global thing. If a JSON api doesn't fill all
>> ecosystem niches, multiple can be used alongside
>> each other.
>>
>> > The root issue with JSON is that you just can’t tell how to interpret
>> any given JSON token
>>
>> The point where this could be an issue is numbers. Once something is
>> identified as a number we can
>>
>> 1. Parse it immediately. Using a long and falling back to a BigInteger.
>> For decimals its harder to know
>> whether to use a double or BigDecimal internally. In the library I've
>> been copy pasting from to build
>> a prototype that last one is an explicit option and it defaults to
>> doubles for the whole parse.
>> 2. Store the string and parse it upon request. We can still model it as a
>> Json.Number, but the
>> work of interpreting is deferred.
>>
>> But in general, making a tree of json values doesn't particularly affect
>> our ability to interpret it
>> in a certain way. That interpretation is just positional. That's just as
>> true as when making assertions
>> in the form of class structure and field types as it is when making
>> assertions in the form of code.[2]
>>
>> record Thing(Instant a) {}
>>
>> // vs.
>>
>> Decoder.field(json, "a", a -> Instant.ofEpochSecond(Decoder.long_(a)))
>>
>> If anything, using a named type as a lookup key for a deserialization
>> function is the less obvious
>> way to do this.
>>
>> > I’m not sure how to square this circle
>> > I don’t like the idea of shipping a non-data-binding JSON API in the
>> core libs.
>>
>> I think the way to cube this rhombus is to find ways to like the idea of
>> a non-data-binding JSON API. ¯\_(ツ)_/¯
>>
>> My personal journey with that is reaching its terminus here I think.
>>
>> Look on the bright side though - there are legit upsides to explicit tree
>> plucking!
>>
>> Yeah, the friction per field is slightly higher, but the relative
>> friction of custom types, or multiple construction methods for a
>> particular type, or maintaining compatibility with
>> legacy representations, or even just handling a top level list of things
>> - its much lower.
>>
>> And all that complexity - that an instant is made by looking for a long
>> or that it is parsed from a string in a
>> particular format - it lives in Java code you can see, touch, feel and
>> taste.
>>
>> I know "nobody does this"[2] but it's not that bad, actually.
>>
>> [1]: I do apologize for the code sketches consistentl

Re: JEP-198 - Lets start talking about JSON

2023-02-28 Thread Ethan McCue
As an update to my character arc, I documented and wrote up an explanation
for the prototype library I was working on.[1]

And I've gotten a good deal of feedback on reddit[2] and in private.

I think its relevant to the conversation here in the sense of

- There are more of rzwitserloot's objections to read on the general
concept JSON as a built in.[3]
- There are a lot of well reasoned objections to the manner in which I am
interpreting a JSON tree, as well
as objections to the usage of a tree as the core. JEP 198's current writeup
(which I know is subject to a rewrite/retraction)
presumes that an immutable tree would be the core data structure.
- The peanut gallery might be interested in a "base" to implement whatever
their take on an API should be.

For that last category, I have a method-handle proxy written up for those
who want to try the "push parser into a pull parser"
transformation I alluded to in my first email of this thread.

[1]: https://mccue.dev/pages/2-26-23-json
[2]:
https://www.reddit.com/r/java/comments/11cyoh1/please_try_my_json_library/
[3]: Including one that reddit took down, but can be seen through reveddit
https://www.reveddit.com/y/rzwitserloot/?after=t1_jacpsj6=1=new=t1_jaa3x0q_status=all

On Fri, Dec 16, 2022 at 6:23 PM Ethan McCue  wrote:

> Sidenote about "Project Galahad" - I know Graal uses json for a few things
> including a reflection-config.json. Food for thought.
>
> > the java.util.log experiment shows that trying to ‘core-librarize’ needs
> that the community at large already fulfills with third party deps isn’t a
> good move,
>
> I, personally, do not have much historical context for java.util.log. What
> feels distinct about providing a JSON api is that
> logging is an implicitly global thing. If a JSON api doesn't fill all
> ecosystem niches, multiple can be used alongside
> each other.
>
> > The root issue with JSON is that you just can’t tell how to interpret
> any given JSON token
>
> The point where this could be an issue is numbers. Once something is
> identified as a number we can
>
> 1. Parse it immediately. Using a long and falling back to a BigInteger.
> For decimals its harder to know
> whether to use a double or BigDecimal internally. In the library I've been
> copy pasting from to build
> a prototype that last one is an explicit option and it defaults to doubles
> for the whole parse.
> 2. Store the string and parse it upon request. We can still model it as a
> Json.Number, but the
> work of interpreting is deferred.
>
> But in general, making a tree of json values doesn't particularly affect
> our ability to interpret it
> in a certain way. That interpretation is just positional. That's just as
> true as when making assertions
> in the form of class structure and field types as it is when making
> assertions in the form of code.[2]
>
> record Thing(Instant a) {}
>
> // vs.
>
> Decoder.field(json, "a", a -> Instant.ofEpochSecond(Decoder.long_(a)))
>
> If anything, using a named type as a lookup key for a deserialization
> function is the less obvious
> way to do this.
>
> > I’m not sure how to square this circle
> > I don’t like the idea of shipping a non-data-binding JSON API in the
> core libs.
>
> I think the way to cube this rhombus is to find ways to like the idea of a
> non-data-binding JSON API. ¯\_(ツ)_/¯
>
> My personal journey with that is reaching its terminus here I think.
>
> Look on the bright side though - there are legit upsides to explicit tree
> plucking!
>
> Yeah, the friction per field is slightly higher, but the relative
> friction of custom types, or multiple construction methods for a
> particular type, or maintaining compatibility with
> legacy representations, or even just handling a top level list of things -
> its much lower.
>
> And all that complexity - that an instant is made by looking for a long or
> that it is parsed from a string in a
> particular format - it lives in Java code you can see, touch, feel and
> taste.
>
> I know "nobody does this"[2] but it's not that bad, actually.
>
> [1]: I do apologize for the code sketches consistently being "what I think
> an interaction with a tree api should look like."
> That is what I have been thinking about for a while so it's hard to resist.
> [2]: https://youtu.be/dOgfWXw9VrI?t=1225
>
> On Thu, Dec 15, 2022 at 6:34 PM Ethan McCue  wrote:
>
>> > are pure JSON parsers really the go-to for most people?
>>
>> Depends on what you mean by JSON parsers and it depends on what you mean
>> by people.
>>
>> To the best of my knowledge, both python and Javascript do not include
>> streaming, databinding, or path navigation

Re: JEP-198 - Lets start talking about JSON

2022-12-16 Thread Ethan McCue
Sidenote about "Project Galahad" - I know Graal uses json for a few things
including a reflection-config.json. Food for thought.

> the java.util.log experiment shows that trying to ‘core-librarize’ needs
that the community at large already fulfills with third party deps isn’t a
good move,

I, personally, do not have much historical context for java.util.log. What
feels distinct about providing a JSON api is that
logging is an implicitly global thing. If a JSON api doesn't fill all
ecosystem niches, multiple can be used alongside
each other.

> The root issue with JSON is that you just can’t tell how to interpret any
given JSON token

The point where this could be an issue is numbers. Once something is
identified as a number we can

1. Parse it immediately. Using a long and falling back to a BigInteger. For
decimals its harder to know
whether to use a double or BigDecimal internally. In the library I've been
copy pasting from to build
a prototype that last one is an explicit option and it defaults to doubles
for the whole parse.
2. Store the string and parse it upon request. We can still model it as a
Json.Number, but the
work of interpreting is deferred.

But in general, making a tree of json values doesn't particularly affect
our ability to interpret it
in a certain way. That interpretation is just positional. That's just as
true as when making assertions
in the form of class structure and field types as it is when making
assertions in the form of code.[2]

record Thing(Instant a) {}

// vs.

Decoder.field(json, "a", a -> Instant.ofEpochSecond(Decoder.long_(a)))

If anything, using a named type as a lookup key for a deserialization
function is the less obvious
way to do this.

> I’m not sure how to square this circle
> I don’t like the idea of shipping a non-data-binding JSON API in the core
libs.

I think the way to cube this rhombus is to find ways to like the idea of a
non-data-binding JSON API. ¯\_(ツ)_/¯

My personal journey with that is reaching its terminus here I think.

Look on the bright side though - there are legit upsides to explicit tree
plucking!

Yeah, the friction per field is slightly higher, but the relative
friction of custom types, or multiple construction methods for a particular
type, or maintaining compatibility with
legacy representations, or even just handling a top level list of things -
its much lower.

And all that complexity - that an instant is made by looking for a long or
that it is parsed from a string in a
particular format - it lives in Java code you can see, touch, feel and
taste.

I know "nobody does this"[2] but it's not that bad, actually.

[1]: I do apologize for the code sketches consistently being "what I think
an interaction with a tree api should look like."
That is what I have been thinking about for a while so it's hard to resist.
[2]: https://youtu.be/dOgfWXw9VrI?t=1225

On Thu, Dec 15, 2022 at 6:34 PM Ethan McCue  wrote:

> > are pure JSON parsers really the go-to for most people?
>
> Depends on what you mean by JSON parsers and it depends on what you mean
> by people.
>
> To the best of my knowledge, both python and Javascript do not include
> streaming, databinding, or path navigation capabilities in their json
> parsers.
>
>
> On Thu, Dec 15, 2022 at 6:26 PM Ethan McCue  wrote:
>
>> > The 95%+ use case for working with JSON for your average java coder is
>> best done with data binding.
>>
>> To be brave yet controversial: I'm not sure this is neccesarily true.
>>
>> I will elaborate and respond to the other points after a hot cocoa, but
>> the last point is part of why I think that tree-crawling needs _something_
>> better as an API to fit the bill.
>>
>> With my sketch that set of requirements would be represented as
>>
>> record Thing(
>> List xs
>> ) {
>> static Thing fromJson(Json json)
>> var defaultList = List.of(0L);
>> return new Thing(Decoder.optionalNullableField(
>> json
>> "xs",
>> Decoder.oneOf(
>> Decoder.array(Decoder.oneOf(
>> x -> Long.parseLong(Decoder.string(x)),
>> Decoder::long
>> ))
>> Decoder.null_(defaultList),
>> x -> List.of(Decoder.long_(x))
>> ),
>> defaultList
>> ));
>> )
>> }
>>
>> Which isn't amazing at first glance, but also
>>
>>{}
>>{"xs": null}
>>{"xs": 5}
>>{"xs": [5]}   {"xs": ["5"]}
>>{"xs": [1, &quo

Re: JEP-198 - Lets start talking about JSON

2022-12-15 Thread Ethan McCue
> are pure JSON parsers really the go-to for most people?

Depends on what you mean by JSON parsers and it depends on what you mean by
people.

To the best of my knowledge, both python and Javascript do not include
streaming, databinding, or path navigation capabilities in their json
parsers.


On Thu, Dec 15, 2022 at 6:26 PM Ethan McCue  wrote:

> > The 95%+ use case for working with JSON for your average java coder is
> best done with data binding.
>
> To be brave yet controversial: I'm not sure this is neccesarily true.
>
> I will elaborate and respond to the other points after a hot cocoa, but
> the last point is part of why I think that tree-crawling needs _something_
> better as an API to fit the bill.
>
> With my sketch that set of requirements would be represented as
>
> record Thing(
> List xs
> ) {
> static Thing fromJson(Json json)
> var defaultList = List.of(0L);
> return new Thing(Decoder.optionalNullableField(
> json
> "xs",
> Decoder.oneOf(
> Decoder.array(Decoder.oneOf(
> x -> Long.parseLong(Decoder.string(x)),
> Decoder::long
> ))
> Decoder.null_(defaultList),
> x -> List.of(Decoder.long_(x))
> ),
> defaultList
> ));
> )
> }
>
> Which isn't amazing at first glance, but also
>
>{}
>{"xs": null}
>{"xs": 5}
>{"xs": [5]}   {"xs": ["5"]}
>{"xs": [1, "2", "3"]}
>
> these are some wildly varied structures. You could make a solid argument
> that something which silently treats these all the same is
> a bad API for all the reasons you would consider it a good one.
>
> On Thu, Dec 15, 2022 at 6:18 PM Johannes Lichtenberger <
> lichtenberger.johan...@gmail.com> wrote:
>
>> I'll have to read the whole thing, but are pure JSON parsers really the
>> go-to for most people? I'm a big advocate of providing also something
>> similar to XPath/XQuery and that's IMHO JSONiq (90% XQuery). I might be
>> biased, of course, as I'm working on Brackit[1] in my spare time (which is
>> also a query compiler and intended to be used with proven optimizations by
>> document stores / JSON stores), but also can be used as an in-memory query
>> engine.
>>
>> kind regards
>> Johannes
>>
>> [1] https://github.com/sirixdb/brackit
>>
>> Am Do., 15. Dez. 2022 um 23:03 Uhr schrieb Reinier Zwitserloot <
>> rein...@zwitserloot.com>:
>>
>>> A recent Advent-of-Code puzzle also made me double check the support of
>>> JSON in the java core libs and it is indeed a curious situation that the
>>> java core libs don’t cater to it particularly well.
>>>
>>> However, I’m not seeing an easy way forward to try to close this hole in
>>> the core library offerings.
>>>
>>> If you need to stream huge swaths of JSON, generally there’s a clear
>>> unit size that you can just databind. Something like:
>>>
>>> String jsonStr = """ { "version": 5, "data": [
>>>   -- 1 million relatively small records in this list --
>>>   ] } """;
>>>
>>>
>>> The usual swath of JSON parsers tend to support this (giving you a
>>> stream of java instances created by databinding those small records one by
>>> one), or if not, the best move forward is presumably to file a pull request
>>> with those projects; the java.util.log experiment shows that trying to
>>> ‘core-librarize’ needs that the community at large already fulfills with
>>> third party deps isn’t a good move, especially if the core library variant
>>> tries to oversimplify to avoid the trap of being too opinionated (which
>>> core libs shouldn’t be). In other words, the need for ’stream this JSON for
>>> me’ style APIs is even more exotic that Ethan is suggesting.
>>>
>>> I see a fundamental problem here:
>>>
>>>
>>>- The 95%+ use case for working with JSON for your average java
>>>coder is best done with data binding.
>>>- core libs doesn’t want to provide it, partly because it’s got a
>>>large design space, partly because the field’s already covered by GSON 
>>> and
>>>Jackson-json; java.util.log proves this doesn’t work. At least, I gather
>>>that’s what Ethan thinks and I 

Re: JEP-198 - Lets start talking about JSON

2022-12-15 Thread Ethan McCue
le library (JSR310’s story, more or less). JSON parsing would
>>qualify as ‘well-established’ (GSON and Jackson) and ‘large design space’
>>as Ethan pointed out.
>>- Given that 99% of java projects, even really simple ones, start
>>with maven/gradle and a list of deps, is that really a problem?
>>
>>
>> I’m honestly not sure what the right answer is. On one hand, the npm
>> ecosystem seems to be doing very well even though their ‘batteries
>> included’ situation is an utter shambles. Then again, the notion that your
>> average nodejs project includes 10x+ more dependencies than other languages
>> is likely a significant part of the security clown fiesta going on over
>> there as far as 3rd party deps is concerned, so by no means should java
>> just blindly emulate their solutions.
>>
>> I don’t like the idea of shipping a non-data-binding JSON API in the core
>> libs. The root issue with JSON is that you just can’t tell how to interpret
>> any given JSON token, because that’s not how JSON is used in practice. What
>> does 5 mean? Could be that I’m to take that as an int, or as a double,
>> or perhaps even as a j.t.Instant (epoch-millis), and defaulting
>> behaviour (similar to j.u.Map’s .getOrDefault is *very* convenient to
>> parse most JSON out there in the real world - omitting k/v pairs whose
>> value is still on default is very common). That’s what makes those databind
>> libraries so enticing: Instead of trying to pattern match my way into this
>> behaviour:
>>
>>
>>- If the element isn’t there at all or null, give me a list-of-longs
>>with a single 0 in it.
>>- If the element is a number, make me a list-of-longs with 1 value in
>>it, that is that number, as long.
>>- If the element is a string, parse it into a long, then get me a
>>list with this one long value (because IEEE double rules mean sometimes 
>> you
>>have to put these things in string form or they get mangled by javascript-
>>eval style parsers).
>>
>>
>> And yet the above is quite common, and can easily be done by a
>> databinder, which sees you want a List for a field whose default
>> value is List.of(1L), and, armed with that knowledge, can transit the
>> JSON into java in that way.
>>
>> You don’t *need* databinding to cater to this idea: You could for
>> example have a jsonNode.asLong(123) method that would parse a string if
>> need be, even. But this has nothing to do with pattern matching either.
>>
>>  --Reinier Zwitserloot
>>
>>
>> On 15 Dec 2022 at 21:30:17, Ethan McCue  wrote:
>>
>>> I'm writing this to drive some forward motion and to nerd-snipe those
>>> who know better than I do into putting their thoughts into words.
>>>
>>> There are three ways to process JSON[1]
>>> - Streaming (Push or Pull)
>>> - Traversing a Tree (Realized or Lazy)
>>> - Declarative Databind (N ways)
>>>
>>> Of these, JEP-198 explicitly ruled out providing "JAXB style type safe
>>> data binding."
>>>
>>> No justification is given, but if I had to insert my own: mapping the
>>> Json model to/from the Java/JVM object model is a cursed combo of
>>> - Huge possible design space
>>> - Unpalatably large surface for backwards compatibility
>>> - Serialization! Boo![2]
>>>
>>> So for an artifact like the JDK, it probably doesn't make sense to
>>> include. That tracks.
>>> It won't make everyone happy, people like databind APIs, but it tracks.
>>>
>>> So for the "read flow" these are the things to figure out.
>>>
>>> | Should Provide? | Intended User(s) |
>>> +-+--+
>>>  Streaming Push | |  |
>>> +-+--+
>>>  Streaming Pull | |  |
>>> +-+--+
>>>  Realized Tree  | |  |
>>> +-+--+
>>>  Lazy Tree  | |  |
>>> +-+--+
>>>
>>> At which point, we should talk about what "meets needs of Java
>>> developers using JSON" implies.
>>>
>>> JSON is ubiquitous. Most kinds of software us schmucks write could have
>>> a reason to interact with it.
&g

JEP-198 - Lets start talking about JSON

2022-12-15 Thread Ethan McCue
I'm writing this to drive some forward motion and to nerd-snipe those who
know better than I do into putting their thoughts into words.

There are three ways to process JSON[1]
- Streaming (Push or Pull)
- Traversing a Tree (Realized or Lazy)
- Declarative Databind (N ways)

Of these, JEP-198 explicitly ruled out providing "JAXB style type safe data
binding."

No justification is given, but if I had to insert my own: mapping the Json
model to/from the Java/JVM object model is a cursed combo of
- Huge possible design space
- Unpalatably large surface for backwards compatibility
- Serialization! Boo![2]

So for an artifact like the JDK, it probably doesn't make sense to include.
That tracks.
It won't make everyone happy, people like databind APIs, but it tracks.

So for the "read flow" these are the things to figure out.

| Should Provide? | Intended User(s) |
+-+--+
 Streaming Push | |  |
+-+--+
 Streaming Pull | |  |
+-+--+
 Realized Tree  | |  |
+-+--+
 Lazy Tree  | |  |
+-+--+

At which point, we should talk about what "meets needs of Java developers
using JSON" implies.

JSON is ubiquitous. Most kinds of software us schmucks write could have a
reason to interact with it.
The full set of "user personas" therefore aren't practical for me to talk
about.[3]

JSON documents, however, are not so varied.

- There are small ones (1-10kb)
- There are medium ones (10-1000kb)
- There are big ones (1000kb-???)

- There are shallow ones
- There are deep ones

So that feels like an easier direction to talk about it from.


This repo[4] has some convenient toy examples of how some of those APIs
look in libraries
in the ecosystem. Specifically the Streaming Pull and Realized Tree models.

User r = new User();
while (true) {
JsonToken token = reader.peek();
switch (token) {
case BEGIN_OBJECT:
reader.beginObject();
break;
case END_OBJECT:
reader.endObject();
return r;
case NAME:
String fieldname = reader.nextName();
switch (fieldname) {
case "id":
r.setId(reader.nextString());
break;
case "index":
r.setIndex(reader.nextInt());
break;
...
case "friends":
r.setFriends(new ArrayList<>());
Friend f = null;
carryOn = true;
while (carryOn) {
token = reader.peek();
switch (token) {
case BEGIN_ARRAY:
reader.beginArray();
break;
case END_ARRAY:
reader.endArray();
carryOn = false;
break;
case BEGIN_OBJECT:
reader.beginObject();
f = new Friend();
break;
case END_OBJECT:
reader.endObject();
r.getFriends().add(f);
break;
case NAME:
String fn = reader.nextName();
switch (fn) {
case "id":

f.setId(reader.nextString());
break;
case "name":

f.setName(reader.nextString());
break;
}
break;
}
}
break;
}
}

I think its not hard to argue that the streaming apis are brutalist. The
above is Gson, but Jackson, moshi, etc
seem at least morally equivalent.

Its hard to write, hard to write *correctly*, and theres is a curious
protensity towards pairing it
with anemic, mutable models.

That being said, it handles big 

Re: Boilerplate to add a new module

2022-12-14 Thread Ethan McCue
Subtract one negative from that last clause

On Wed, Dec 14, 2022 at 12:02 PM Ethan McCue  wrote:

> Thank you!
>
> And yeah - I'll send a separate email opining on the directions that can
> go.
>
> The features that should be incorporated/designed for (value
> classes/string templates/pattern matching) are more clearly in view than
> they were three years ago when last it was brought up and certainly more
> real than in 2014 when the JEP was made - so I don't think this isn't a
> horrible time to start some movement.
>
> On Wed, Dec 14, 2022 at 3:14 AM Alan Bateman 
> wrote:
>
>> On 14/12/2022 03:36, Ethan McCue wrote:
>> > Hey all,
>> >
>> > I'm doodling on JEP-198, and I was wondering if anyone had a line on
>> > the boilerplate needed to add a new module to the build.
>> >
>> > Just adding
>> >
>> > src/
>> >   java.json/
>> > share/
>> >   classes/
>> > module-info.java
>> > java/
>> >   json/
>> > Json.java
>> > ...
>> >
>> > was enough to get it to show up under the output when I run "make
>> > images", but I can't shake the feeling that I'm missing something.
>>
>> Modules with java.* classes must be mapped to the boot or platform class
>> loader. That mapping is defined in make/conf/module-loader-map.conf.
>>
>> java.se is the aggregator module for the APIs for ava SE platform.
>> You'll see it has a `requires transitive` for each of the standard
>> modules.
>>
>> That should be enough to get your doodle going. As per previous mails,
>> it's very likely that JEP-198 will be withdrawn or replaced with a new
>> JEP, when the time comes.
>>
>> -Alan.
>>
>


Re: Boilerplate to add a new module

2022-12-14 Thread Ethan McCue
Thank you!

And yeah - I'll send a separate email opining on the directions that can
go.

The features that should be incorporated/designed for (value classes/string
templates/pattern matching) are more clearly in view than they were three
years ago when last it was brought up and certainly more real than in 2014
when the JEP was made - so I don't think this isn't a horrible time to
start some movement.

On Wed, Dec 14, 2022 at 3:14 AM Alan Bateman 
wrote:

> On 14/12/2022 03:36, Ethan McCue wrote:
> > Hey all,
> >
> > I'm doodling on JEP-198, and I was wondering if anyone had a line on
> > the boilerplate needed to add a new module to the build.
> >
> > Just adding
> >
> > src/
> >   java.json/
> > share/
> >   classes/
> > module-info.java
> > java/
> >   json/
> > Json.java
> > ...
> >
> > was enough to get it to show up under the output when I run "make
> > images", but I can't shake the feeling that I'm missing something.
>
> Modules with java.* classes must be mapped to the boot or platform class
> loader. That mapping is defined in make/conf/module-loader-map.conf.
>
> java.se is the aggregator module for the APIs for ava SE platform.
> You'll see it has a `requires transitive` for each of the standard modules.
>
> That should be enough to get your doodle going. As per previous mails,
> it's very likely that JEP-198 will be withdrawn or replaced with a new
> JEP, when the time comes.
>
> -Alan.
>


Boilerplate to add a new module

2022-12-13 Thread Ethan McCue
Hey all,

I'm doodling on JEP-198, and I was wondering if anyone had a line on the
boilerplate needed to add a new module to the build.

Just adding

src/
  java.json/
share/
  classes/
module-info.java
java/
  json/
Json.java
...

was enough to get it to show up under the output when I run "make images",
but I can't shake the feeling that I'm missing something.

(jtreg I assume has some extra setup? Anything else?)


Re: What is meant by "document context" in JEP 198?

2022-11-25 Thread Ethan McCue
That tracks.

The component I, personally, might want JSON available internally for is an
artifact resolution API [1]. Gradle's module metadata is out there in the
world and could be worth consuming [2]. That's neither here nor there
though.

I'd say that the JEP as written could use some rework even before its "time
has come".

* Goals like "useful subset for Java ME" don't feel relevant anymore.
* Unstated goals that seem implicit in how folks have talked about
potential APIs, like "improve over JSR-353" or "obviate
ecosystem desire for JAXB type data binding"
* "Goals" that seem derived more from the properties of whatever prototype
implementation the JEP had than a core "user story". A split
token/event/immutable tree API is just one possible solution to problems
like big documents and letting power users avoid allocations.

[1]:
https://mail.openjdk.org/pipermail/core-libs-dev/2022-September/094231.html
[2]:
https://docs.gradle.org/current/userguide/publishing_gradle_module_metadata.html

On Fri, Nov 25, 2022 at 9:08 AM Alan Bateman 
wrote:

> On 25/11/2022 13:27, Ethan McCue wrote:
>
> ...huh
>
> >>  The recent removal of Nashorn has indicated several places were
> >> javascript were used in the JDK mainly due to it's built-in support for
> >> parsing JSON
> None of those seem like they would have been using nashorn in the past, so i 
> wonder what this is referring to.
>
>
> The quoted text seems to be from a mail that Magnus Ihse Bursie sent to
> the discuss list in 2020 [1]. It's the same date that JDK-8242934 [1] was
> created so it may be that the mail was (at least partly) prompted by the
> need to change the test  for`jfr print --json`. The test for that feature
> used to use Nashorn and had to be changed to use a JSON parser in the test
> suite. The JSON parser was subsequently moved to
> test/lib/jdk/test/lib/json/JSONValue.java so it could be used by other
> tests.
>
> -Alan
>
> [1] https://mail.openjdk.org/pipermail/discuss/2020-April/005398.html
> [2] https://bugs.openjdk.org/browse/JDK-8242934
>


Re: What is meant by "document context" in JEP 198?

2022-11-25 Thread Ethan McCue
...huh

>>  The recent removal of Nashorn has indicated several places were
>> javascript were used in the JDK mainly due to it's built-in support for
>> parsing JSON
None of those seem like they would have been using nashorn in the
past, so i wonder what this is referring to.


On Fri, Nov 25, 2022 at 2:52 AM Alan Bateman 
wrote:

> On 25/11/2022 02:04, Ethan McCue wrote:
> > I suppose while I'm asking questions - what exactly are the parts of
> > the JDK making use of ad-hoc json? Maybe we could ship *something* as
> > a jdk.internal module for those use cases?
> >
> JEP 198 pre-dates records, sealed classes, pattern matching, ... and I
> will assume will re-drafted or replaced when the time comes.
>
> Right now, I don't think there are too many places in the JDK that
> interact with JSON.  The `jfr` tool can print the records from a JFR
> recording as a JSON document. The HotSpotDiagnosMXBean API and `jcmd`
> tool can generate a thread dump as a JSON document. I think the only
> parsing at this time is the HotSpot compiler control (JEP 165) where
> directives file is a subset of JSON but that is implemented in C++ in
> libjvm and probably not what you are looking for.
>
> There are number of places in the JDK that read configuration and it
> might be that some of these could consume configuration in JSON in the
> future.
>
> -Alan
>


Re: What is meant by "document context" in JEP 198?

2022-11-24 Thread Ethan McCue
I suppose while I'm asking questions - what exactly are the parts of the
JDK making use of ad-hoc json? Maybe we could ship *something* as a
jdk.internal module for those use cases?

On Thu, Nov 24, 2022 at 8:55 PM Ethan McCue  wrote:

> I'm reading JEP 198 and sketching out what an implementation could look
> like pursuant to this old conversation.
>
> https://mail.openjdk.org/pipermail/discuss/2020-April/005401.html
>
> My biggest question right now is what does the JEP mean exactly by
> "document context"
>
>
>- Parsing APIs which allow a choice of parsing token stream, event
>(includes document hierarchy context) stream, or immutable tree
>representation views of JSON documents and data streams.
>
>
>
> So token stream I understand as something akin to
>
> sealed interface Token {
> record StartArray() implements Token {}
> record EndArray() implements Token {}
> record StartObject() implements Token {}
> record EndObject() implements Token {}
> record Number(Json.Number value) implements Token {}
> record String(Json.String value) implements Token {}
> record True() implements Token {}
> record False() implements Token {}
> record Null() implements Token {}
> }
>
> Which matches up with the model in
> https://fasterxml.github.io/jackson-core/javadoc/2.7/com/fasterxml/jackson/core/JsonToken.html
>
> And an immutable tree representation as akin to
>
> sealed interface Json {
> sealed interface Array extends Json, List ...
> sealed interface Object extends Json, Map ...
> sealed abstract class Number extends java.lang.Number implements Json
> ...
> sealed interface String extends Json, CharSequence ...
> sealed interface Boolean ...
> sealed interface Null extends Json ...
> }
>
> Which, ignoring the open questions of
> * Does the immutable tree need to be eagerly realized?
> * Do we need to wait for valhalla to land
> * Do we need to wait for full pattern matching to land
>
> (because they make me sad)
>
> I'm still unsure what information needs to be included in an "Event"
> stream that would constitute "document context". Is it pointers to parent
> collections? The current "Path"?
>
> sealed interface PathElement {
> record Field(String fieldName) implements PathElement {}
> record Index(int index) implements PathElement {}
> }
>
>
>
>


What is meant by "document context" in JEP 198?

2022-11-24 Thread Ethan McCue
I'm reading JEP 198 and sketching out what an implementation could look
like pursuant to this old conversation.

https://mail.openjdk.org/pipermail/discuss/2020-April/005401.html

My biggest question right now is what does the JEP mean exactly by
"document context"


   - Parsing APIs which allow a choice of parsing token stream, event
   (includes document hierarchy context) stream, or immutable tree
   representation views of JSON documents and data streams.



So token stream I understand as something akin to

sealed interface Token {
record StartArray() implements Token {}
record EndArray() implements Token {}
record StartObject() implements Token {}
record EndObject() implements Token {}
record Number(Json.Number value) implements Token {}
record String(Json.String value) implements Token {}
record True() implements Token {}
record False() implements Token {}
record Null() implements Token {}
}

Which matches up with the model in
https://fasterxml.github.io/jackson-core/javadoc/2.7/com/fasterxml/jackson/core/JsonToken.html

And an immutable tree representation as akin to

sealed interface Json {
sealed interface Array extends Json, List ...
sealed interface Object extends Json, Map ...
sealed abstract class Number extends java.lang.Number implements Json
...
sealed interface String extends Json, CharSequence ...
sealed interface Boolean ...
sealed interface Null extends Json ...
}

Which, ignoring the open questions of
* Does the immutable tree need to be eagerly realized?
* Do we need to wait for valhalla to land
* Do we need to wait for full pattern matching to land

(because they make me sad)

I'm still unsure what information needs to be included in an "Event" stream
that would constitute "document context". Is it pointers to parent
collections? The current "Path"?

sealed interface PathElement {
record Field(String fieldName) implements PathElement {}
record Index(int index) implements PathElement {}
}


Re: What are the policies for internal modules?

2022-11-24 Thread Ethan McCue
(always so hard to remember to reply all)

On Thu, Nov 24, 2022 at 8:26 PM Ethan McCue  wrote:

> The use here is giving help to the user on
> misspelled classes/methods/fields like "new ArayList".
>
> For now we've just copy pasted the class since that seems the least
> architecturally painful. (And it's not clear that text distance is the full
> story with regards to this sort of analysis, so tbd if that stays in its
> current form)
>
> On Mon, Nov 21, 2022 at 3:19 AM Alan Bateman 
> wrote:
>
>> On 21/11/2022 01:02, Ethan McCue wrote:
>> > There are some modules like jdk.internal.le which contain no
>> > publicly exported packages. In some of the experimentation we are
>> > doing, We want to use the
>> > jdk.internal.org.jline.utils.Levenshtien class from jdk.compiler.
>> >
>> > Mechanically, we can do this without creating any new modules by
>> > adding a qualified export of the utils package to jdk.compiler. We
>> > could also make a brand new, tiny pointless module called
>> > jdk.internal.levenshtien. That would be the "cleanest" approach, but
>> > only if we didn't consider the existence of the internal modules to be
>> > public API.
>> >
>> > So that's my general question - what is the bar for making a new
>> > internal module, and is the set of internal modules subject to
>> > backwards compatibility concerns?
>>
>> There aren't many compatibility constraints on jdk.internal modules.
>> Assume they can change or be removed in any release. It's important they
>> don't export any packages to all modules, otherwise they cease to be
>> "internal".  Also if they provide services then the name of the module
>> may be something that users of jlink may become dependent on, so we have
>> to be careful there.
>>
>> A general point is that we don't want the JDK to backslide to a big ball
>> of mud. We put a lot of effort several years ago to de-tangle the
>> libraries so it would be disappointing see jdk.compiler, with no
>> interest in line editing, add a dependency on jdk.internal.le so that it
>> can make use of one of utility classes. So I don't think we should do
>> that.
>>
>> Can you say a bit more about what you are doing? Is this just a method
>> to compute the Levenshtein distance between two strings? I assume you
>> can just implement that in jdk.compiler without touching the module
>> graph. It might be useful to get some sense on how common fuzzy matching
>> is in the eco system to see if there is any merit to exposing APIs for
>> this.
>>
>> -Alan
>>
>>


What are the policies for internal modules?

2022-11-20 Thread Ethan McCue
There are some modules like jdk.internal.le which contain no
publicly exported packages. In some of the experimentation we are doing, We
want to use the jdk.internal.org.jline.utils.Levenshtien class from
jdk.compiler.

Mechanically, we can do this without creating any new modules by adding a
qualified export of the utils package to jdk.compiler. We could also make a
brand new, tiny pointless module called jdk.internal.levenshtien. That
would be the "cleanest" approach, but only if we didn't consider the
existence of the internal modules to be public API.

So that's my general question - what is the bar for making a new internal
module, and is the set of internal modules subject to backwards
compatibility concerns?


Re: Task for an aspiring JDK contributor

2022-11-18 Thread Ethan McCue
The problem is that would necessitate a new public class that is *only*
used as a return value. Since it would then come up with auto complete for
people searching for ArrayList, that's non trivial.

Deprecated also isn't the right concept. Mechanically it puts a line
through a method invocation, but this is a more specific sort of
disclaimer. Like a @Unsupported.

There is a whole separate universe of pitches for more standard
annotations, but efforts like that would probably have to start in the
community like jspecify is attempting for nullness annotations.

On Fri, Nov 18, 2022, 12:47 PM Andreas Røsdal 
wrote:

> How about this as a proposed solution to avoid
> UnsupportedOperationException:
> 1. Renamed the internal ArrayList to ArrayWrappingList
> 2. Override add() in ArrayWrappingList and make it deprecated to warn
> people from using it.
>
>
> On Fri, Nov 18, 2022 at 6:23 PM Ethan McCue  wrote:
>
>> I think there is potentially actionable feedback in that the exception
>> thrown when users bump into this limitation kinda sucks
>>
>> jshell> Arrays.asList(1, 2, 3).add(4);
>> |  Exception java.lang.UnsupportedOperationException
>> |at AbstractList.add (AbstractList.java:155)
>> |at AbstractList.add (AbstractList.java:113)
>> |at (#8:1)
>>
>> jshell> try { Arrays.asList(1, 2, 3).add(4);} catch (Exception e) {
>> System.out.println(e.getMessage());}
>> null
>>
>> I think there would be value in overriding "add" and other such
>> operations to provide enough context for users to *understand* that
>> * they did an unsupported operation
>> * the list given by `Arrays.asList` was the cause
>>
>> If I had to guess, that's the core of the frustration. The process to get
>> from "not understanding what went wrong" -> "understanding what went wrong"
>> -> "knowing how to fix it" is high.
>>
>> The design problem is how much context can be conveyed in an exception
>> message/stack trace.
>>
>> There is a similar conversation to be had for the collections returned by
>> List.of() and similar.
>>
>> jshell> List.of().add(1)
>> |  Exception java.lang.UnsupportedOperationException
>> |at ImmutableCollections.uoe (ImmutableCollections.java:142)
>> |at ImmutableCollections$AbstractImmutableCollection.add
>> (ImmutableCollections.java:147)
>> |at (#6:1)
>>
>> jshell> try { List.of(1, 2, 3).add(4);} catch (Exception e) {
>> System.out.println(e.getMessage());}
>> null
>>
>> There is a clue in the stack trace here though for List.of() with the
>> "ImmutableCollections" calls. Maybe if we took two moves
>>
>> 1. Renamed the internal ArrayList to something like ArrayWrappingList
>> 2. Overrode add
>>
>> then the stack trace could be enough (or better than the status quo)
>>
>> jshell> Arrays.asList(1, 2, 3).add(4);
>> |  Exception java.lang.UnsupportedOperationException
>> |at ArrayWrappingList.add (ArrayWrappingList.java:155)
>> |at ArrayWrappingList.add (ArrayWrappingList.java:113)
>> |at (#8:1)
>>
>> On Fri, Nov 18, 2022 at 12:14 PM Andreas Røsdal 
>> wrote:
>>
>>> `new ArrayList<>(Arrays.asList(array))`  is quite complex syntax to
>>> convert an array to an java.util.ArrayList,
>>> so a suggestion could be to add a new method to Arrays to convert an
>>> array to a normal java.util.ArrayList which is modifiable.
>>>
>>> Are there any low-hanging-fruit issues in core-libs-dev in
>>> bugs.openjdk.org that you are aware of that
>>> you would like me to help you implement?
>>>
>>> Thanks for considering my request.
>>>
>>> Regards,
>>> Andreas
>>>
>>>
>>>
>>>
>>> On Fri, Nov 18, 2022 at 5:51 PM Daniel Fuchs 
>>> wrote:
>>>
>>>> Hi Andreas,
>>>>
>>>> First of all, congratulations for seeking advice before working on
>>>> a PR. This is exactly how first contributions should start. Thank
>>>> you for that!
>>>>
>>>> Given the area in which you intended to work however, `core-libs-dev`
>>>> might have been a better list than `discuss` to start from.
>>>>
>>>> With regard to the meat of the issue however, and as noted by Ethan,
>>>> Arrays.asList() behaves as intended, and changing that would be a
>>>> major incompatible change, as many users of the API expect the list
>>>> returned by Arra

Re: Task for an aspiring JDK contributor

2022-11-18 Thread Ethan McCue
I think there is potentially actionable feedback in that the exception
thrown when users bump into this limitation kinda sucks

jshell> Arrays.asList(1, 2, 3).add(4);
|  Exception java.lang.UnsupportedOperationException
|at AbstractList.add (AbstractList.java:155)
|at AbstractList.add (AbstractList.java:113)
|at (#8:1)

jshell> try { Arrays.asList(1, 2, 3).add(4);} catch (Exception e) {
System.out.println(e.getMessage());}
null

I think there would be value in overriding "add" and other such operations
to provide enough context for users to *understand* that
* they did an unsupported operation
* the list given by `Arrays.asList` was the cause

If I had to guess, that's the core of the frustration. The process to get
from "not understanding what went wrong" -> "understanding what went wrong"
-> "knowing how to fix it" is high.

The design problem is how much context can be conveyed in an exception
message/stack trace.

There is a similar conversation to be had for the collections returned by
List.of() and similar.

jshell> List.of().add(1)
|  Exception java.lang.UnsupportedOperationException
|at ImmutableCollections.uoe (ImmutableCollections.java:142)
|at ImmutableCollections$AbstractImmutableCollection.add
(ImmutableCollections.java:147)
|at (#6:1)

jshell> try { List.of(1, 2, 3).add(4);} catch (Exception e) {
System.out.println(e.getMessage());}
null

There is a clue in the stack trace here though for List.of() with the
"ImmutableCollections" calls. Maybe if we took two moves

1. Renamed the internal ArrayList to something like ArrayWrappingList
2. Overrode add

then the stack trace could be enough (or better than the status quo)

jshell> Arrays.asList(1, 2, 3).add(4);
|  Exception java.lang.UnsupportedOperationException
|at ArrayWrappingList.add (ArrayWrappingList.java:155)
|at ArrayWrappingList.add (ArrayWrappingList.java:113)
|at (#8:1)

On Fri, Nov 18, 2022 at 12:14 PM Andreas Røsdal 
wrote:

> `new ArrayList<>(Arrays.asList(array))`  is quite complex syntax to
> convert an array to an java.util.ArrayList,
> so a suggestion could be to add a new method to Arrays to convert an array
> to a normal java.util.ArrayList which is modifiable.
>
> Are there any low-hanging-fruit issues in core-libs-dev in
> bugs.openjdk.org that you are aware of that
> you would like me to help you implement?
>
> Thanks for considering my request.
>
> Regards,
> Andreas
>
>
>
>
> On Fri, Nov 18, 2022 at 5:51 PM Daniel Fuchs 
> wrote:
>
>> Hi Andreas,
>>
>> First of all, congratulations for seeking advice before working on
>> a PR. This is exactly how first contributions should start. Thank
>> you for that!
>>
>> Given the area in which you intended to work however, `core-libs-dev`
>> might have been a better list than `discuss` to start from.
>>
>> With regard to the meat of the issue however, and as noted by Ethan,
>> Arrays.asList() behaves as intended, and changing that would be a
>> major incompatible change, as many users of the API expect the list
>> returned by Arrays.asList to be immutable (and depend on it).
>> It is not possible nor desirable to change that.
>>
>> As for your observation, I believe that:
>>
>>`new ArrayList<>(Arrays.asList(array))`
>>
>> will get you what you want.
>>
>> best regards,
>>
>> -- daniel
>>
>> On 18/11/2022 16:29, Andreas Røsdal wrote:
>> > Yes, the exception comes when adding objects to the returned list. So I
>> > would like a convenient way to use Arrays to convert an array to a
>> > normal modifiable java.util.ArrayList, instead of this AbstractList.
>> >
>> >
>> > On Fri, Nov 18, 2022 at 5:23 PM Ethan McCue > > <mailto:et...@mccue.dev>> wrote:
>> >
>> > What situation were you encountering the exception? Was it when
>> > trying to add to the returned list?
>> >
>> > If so, that's expected. Arrays.asList only wraps an underlying
>> > array, it can't grow it. By that measure List.of() is even more
>> > unintuitive because you can't set anything.
>> >
>> > On Fri, Nov 18, 2022, 11:06 AM Andreas Røsdal
>> > mailto:andreas.ros...@gmail.com>> wrote:
>> >
>> > Hello!
>> >
>> > I am an aspiring JDK contributor, having used Java in my work as
>> > a developer.
>> >
>> > I was recently surprised by an Exception thrown when using the
>> > java.util.Arrays.asList() method
>> > so I would like to propose 

Re: Task for an aspiring JDK contributor

2022-11-18 Thread Ethan McCue
new ArrayList<>(Arrays.asList(theArray));

I'm not intending to be pithy - just this is one of the "known
deficiencies" of the collections framework. If you look hard enough you can
find some spitballing I did on how to address it in the core-libs mailing
list (which would be the place to go for this sort of stuff - cc-ing).

The internal class names aren't trivial to fix either because someone
somewhere has serialized a java.util.Arrays$ArrayList using
ObjectOutputStream and wants to read it back out at some point. Not
impossible to address, just tricky.

What is the situation where you encountered the internal class name? It
could be worth it to make it more clear in whatever that debug scenario is.

---

Other than that, to contribute code to the JDK you need to sign the OCA. It
will take a bit so you should get that out of the way.

On Fri, Nov 18, 2022 at 11:29 AM Andreas Røsdal 
wrote:

> Yes, the exception comes when adding objects to the returned list. So I
> would like a convenient way to use Arrays to convert an array to a normal
> modifiable java.util.ArrayList, instead of this AbstractList.
>
>
> On Fri, Nov 18, 2022 at 5:23 PM Ethan McCue  wrote:
>
>> What situation were you encountering the exception? Was it when trying to
>> add to the returned list?
>>
>> If so, that's expected. Arrays.asList only wraps an underlying array, it
>> can't grow it. By that measure List.of() is even more unintuitive because
>> you can't set anything.
>>
>> On Fri, Nov 18, 2022, 11:06 AM Andreas Røsdal 
>> wrote:
>>
>>> Hello!
>>>
>>> I am an aspiring JDK contributor, having used Java in my work as a
>>> developer.
>>>
>>> I was recently surprised by an Exception thrown when using the
>>> java.util.Arrays.asList() method
>>> so I would like to propose some improvements to this API. In particular:
>>> - When using java.util.Arrays.asList() then AbstractList is throwing 
>>> UnsupportedOperationException
>>> which is not user-friendly or intuitive.
>>> -  java.util.Arrays.asList() returning a private class called ArrayList,
>>> which is not the usual java.util.ArrayList, so it's not user-friendly or
>>> intuitive.
>>>
>>> Since this would be my first contribution to the JDK, and the goal is to
>>> complete the contribution with accepted pull request and get the Arrays API
>>> improved, what would the first step to making this first improvement to the
>>> JDK be?
>>>
>>> I would also like to share a link to an open source project I've been
>>> working on:
>>> https://github.com/fciv-net/fciv-net
>>>
>>> Thank you!
>>>
>>> Regards,
>>> Andreas R.
>>>
>>


Re: Standard Artifact Resolution API

2022-09-10 Thread Ethan McCue
Okay so from what I can parse there are different streams of work here

1. The JDK could be well served by a "standard" build tool.

I don't think this is controversial, but I'm nowhere near qualified enough
to say what the "right" choice is. Maybe it is just taking gradle, who
knows.

What I do posit, though, is that the problem space is underexplored. I will
claim this is partially because things like dependency resolution have been
out of reach of the kinds of low stakes amateur exploration that drives
innovation. The Node ecosystem churns through a build system a season, but
they are at least actively exploring options - y'know?

Let's call this hypothetical tool "jproject".

2. The JDK has the power to standardize improvements to dependency metadata
and resolution

The gradle team has done what they think is a good extension of the
classifier system. There is some other analogous work in the rust ecosystem
around features that could be interesting to incorporate. Maybe even
ABI-level tool enforced minimum semver bumps like Elm, where we make
something similar to Scala's Tasty metadata.

Of all the players in the ecosystem, the JDK probably has the most power to
make a given answer "happen", and being comfortable that the right answer
has been reached may or may not be a blocker for "jproject" being real.

3. A standard dependency resolution api

In order to successfully drive improvements to dependency resolution the
JDK needs a tool, but I don't think that needs to be dependent on either
knowing the "best-path-forward" metadata or deciding on a JDK build tool.

There is enough prior work in tools like coursier (https://get-coursier.io/),
shrinkwrap resolver (https://github.com/shrinkwrap/resolver), maven aether (
https://maven.apache.org/aether.html), and tools deps (
https://github.com/clojure/tools.deps.alpha) to at least start to talk
about what a "jresolve" cli tool or "java.util.resolve"' api could look
like.

(apologies if that is only mildly coherent)

On Sat, Sep 10, 2022 at 12:19 PM Scott Palmer  wrote:

> After thinking about it a bit more, I agree that a canonical dependency
> format wouldn’t be much good without the tool to go with it.  In my
> particular case, I have builds that incorporate native code and Java.  I
> use Gradle partly because it handles both (though it could do a better job
> with C and cross-compiling), and also because it can handle complex build
> logic for putting the pieces together.  One thing I dislike about tools
> such as Rusts’ cargo or Node’s npm, is that they seem to make the
> assumption that my project is based on a single language. That’s one reason
> I don’t use Maven - it assumes you have a vanilla Java project and is
> painful to use for anything beyond that.  Any tool integrated into the JDK
> should be able to build the JDK itself, including all the native bits, and
> dealing with the multiple platforms and conditions.  There should be no
> need for external scripts to hack it together and very few external tools.
> E.g. a C/C++ compiler would be needed to b build the JDK, but not
> bash/Cygwin.
>
> I’ve often thought, why don’t they just use Gradle for building the JDK?
> It seems to me that it would be so much simpler (and faster), but I can
> imagine the mess of build-logic history that would need to be untangled.
> I haven’t looked into alternatives like Bazel, as Gradle is working well
> for me, but in the design of any tool for the JDK all of these other tools
> should be looked at.  Perhaps in the end the JDK could simply settle on one
> of the modern tools already available and integrate or endorse it?  That to
> me would make more sense than building yet another tool (
> https://xkcd.com/927/). For example, if the JDK were to adopt Gradle as
> the tool to use to build the JDK, it could de-facto become the “JDK build
> tool”.   Other projects formerly part of the JDK, like JavaFX, are already
> using Gradle (though not as effectively as they could).
>
> Anyway, that’s just my 2 cents on the subject.
>
> Regards,
>
> Scott
>
>
> On Sep 9, 2022, at 1:31 PM, Ethan McCue  wrote:
>
> > We already have Ant+Ivy, Maven, Gradle and they have significantly
> different philosophies such that agreeing on a single dependency management
> tool may be too ambitious.
>
> Maybe it would be useful to dig into what those different philosophies are?
>
> > I suggest looking at what Gradle has done in this area.
> > It would be a reasonable goal for Java to have a canonical format (like
> Rust’s ‘cargo’ format) for external dependencies that addressed all the
> issues and tools could use it and benefit from the potential cross-tool
> compatibility.
>
> I don't disagree per-se, but without an actual tool the JDK doesn'

Re: Standard Artifact Resolution API

2022-09-09 Thread Ethan McCue
> We already have Ant+Ivy, Maven, Gradle and they have significantly
different philosophies such that agreeing on a single dependency management
tool may be too ambitious.

Maybe it would be useful to dig into what those different philosophies are?

> I suggest looking at what Gradle has done in this area.
> It would be a reasonable goal for Java to have a canonical format (like
Rust’s ‘cargo’ format) for external dependencies that addressed all the
issues and tools could use it and benefit from the potential cross-tool
compatibility.

I don't disagree per-se, but without an actual tool the JDK doesn't exactly
have much leverage to drive adoption of whatever
dependency-metadata.[xml|json|toml|tar.gz] would address all the issues. It
also would still need to handle "the world as is" for published artifacts.

> agreeing on a single dependency management tool may be too ambitious.

Maybe, but an uncharitable combination of accepting both the statements

"it's nearly impossible to write a modern application without external
dependencies"

and

"the jdk does not provide the ability to resolve external dependencies"

is

"it's nearly impossible to write a modern application with just the jdk"

Which is at least a tad unfortunate.

On Fri, Sep 9, 2022 at 12:22 PM Scott Palmer  wrote:

> I suggest looking at what Gradle has done in this area.  For example they
> found that the POM format didn’t have information required to properly
> identify variants so they added additional metadata.  (See:
> https://docs.gradle.org/current/userguide/variant_model.html)
>
> It would be a reasonable goal for Java to have a canonical format (like
> Rust’s ‘cargo’ format) for external dependencies that addressed all the
> issues and tools could use it and benefit from from the potential
> cross-tool compatibility.  I think however that the focus would be on the
> repository format & metadata, not the tool.  We already have Ant+Ivy,
> Maven, Gradle and they have significantly different philosophies such that
> agreeing on a single dependency management tool may be too ambitious.
>
> Scott
>
>
> On Sep 9, 2022, at 9:59 AM, Ethan McCue  wrote:
>
> This email is mostly to test the waters, I expect some hostility.
>
> Say, as a premise, that an API for resolving external dependencies was
> something that the JDK concerned itself with providing.
>
> I think the foundation for that is there - the POM v4 format is more or
> less around forever, maven style repositories aren't going anywhere, and
> it's nearly impossible to write a modern application without some degree of
> dependence on code that was written by other people.
>
> What properties would folks want from such an API? What blockers would
> there be (technical/social)? Any other initial thoughts?
>
>
>


Standard Artifact Resolution API

2022-09-09 Thread Ethan McCue
This email is mostly to test the waters, I expect some hostility.

Say, as a premise, that an API for resolving external dependencies was
something that the JDK concerned itself with providing.

I think the foundation for that is there - the POM v4 format is more or
less around forever, maven style repositories aren't going anywhere, and
it's nearly impossible to write a modern application without some degree of
dependence on code that was written by other people.

What properties would folks want from such an API? What blockers would
there be (technical/social)? Any other initial thoughts?


Re: Proposal: Collection mutability marker interfaces

2022-08-26 Thread Ethan McCue
If the collections would decide whether or not to copy, I don't think just
requesting an immutable reference would be enough.

static  List listCopy(Collection coll) {
if (coll instanceof List12 || (coll instanceof ListN && !
((ListN)coll).allowNulls)) {
return (List)coll;
} else {
return (List)List.of(coll.toArray()); // implicit nullcheck
of coll
}
}

The two things that List.copyOf needs to know are that the list is
immutable, but also that it isn't a variant that might contain a null.

So maybe instead of

 List y = x.immutableCopy();

It could be appropriate to use the spliterator approach and request a copy
which has certain characteristics.

static  List listCopy(Collection coll) {
if (coll instanceof List list) {
return list.copyWhere(EnumSet.of(IMMUTABLE, DISALLOW_NULLS));
} else {
return (List)List.of(coll.toArray()); // implicit nullcheck
of coll
}
}

but that leaves open whether you would want to request the *presence* of
capabilities or the *absence* of them.

Maybe

List.of().copyWhere();

Could be defined to give a list where it is immutable and nulls aren't
allowed. And then

   List.of(1, 2, 3).copyWhere(EnumSet.of(ADDABLE, NULLS_ALLOWED));

gives you a mutable copy where nulls are allowed.

This still does presume that making a copy if a capability isn't present is
the only use of knowing the capabilities - which from the conversation so
far isn't that unrealistic

On Fri, Aug 26, 2022 at 11:20 AM John Hendrikx 
wrote:

>
> On 24/08/2022 15:38, Ethan McCue wrote:
>
> A use case that doesn't cover is adding to a collection.
>
> Say as part of a method's contract you state that you take ownership of a
> List. You aren't going to copy even if the list is mutable.
>
> Later on, you may want to add to the list. Add is supported on ArrayList
> so you don't need to copy and replace your reference, but you would if the
> list you were given was made with List.of or Arrays.asList
>
> I don't think this is a common enough use case that should be catered
> for.  It might be better handled with concurrent lists instead.
>
> The most common use case by far is wanting to make sure a collection
> you've received is not going to be modified while you are working with it.
> I don't think another proposal which does cover the most common cases
> should be dismissed out of hand because it doesn't support a rather rare
> use case.
> --John
>
>
>
> On Wed, Aug 24, 2022, 8:13 AM John Hendrikx 
> wrote:
>
>> Would it be an option to not make the receiver responsible for the
>> decision whether to make a copy or not?  Instead put this burden (using
>> default methods) on the various collections?
>>
>> If List/Set/Map had a method like this:
>>
>>  List immutableCopy();  // returns a (shallow) immutable copy if
>> list is mutable (basically always copies, unless proven otherwise)
>>
>> Paired with methods on Collections to prevent collections from being
>> modified:
>>
>>  Collections.immutableList(List)
>>
>> This wrapper is similar to `unmodifiableList` except it implements
>> `immutableCopy` as `return this`.
>>
>> Then for the various scenario's, where `x` is an untrusted source of List
>> with unknown status:
>>
>>  // Create a defensive copy; result is a private list that cannot be
>> modified:
>>
>>  List y = x.immutableCopy();
>>
>>  // Create a defensive copy for sharing, promising it won't ever
>> change:
>>
>>  List y = Collections.immutableList(x.immutableCopy());
>>
>>  // Create a defensive copy for mutating:
>>
>>  List y = new ArrayList<>(x);  // same as always
>>
>>  // Create a mutable copy, modify it, then expose as immutable:
>>
>>  List y = new ArrayList<>(x);  // same as always
>>
>>  y.add(  );
>>
>>  List z = Collections.immutableList(y);
>>
>>  y = null;  // we promise `z` won't change again by clearing the only
>> path to mutating it!
>>
>> The advantage would be that this information isn't part of the type
>> system where it can easily get lost. The actual implementation knows best
>> whether a copy must be made or not.
>>
>> Of course, the immutableList wrapper can be used incorrectly and the
>> promise here can be broken by keeping a reference to the original (mutable)
>> list, but I think that's an acceptable trade-off.
>>
>> --John
>>
>> PS. Chosen names are just for illustration; there is some discussion as
>> what "unmodifiable" vs "immutable" means in the

Re: Proposal: Collection mutability marker interfaces

2022-08-26 Thread Ethan McCue
All trivial collection operations scream stream api and all stream api
operations imply a full copy or at least a full scan.

> so having trouble to write this kind of code is more a feature than an
issue :)

I love all Java code equally

On Wed, Aug 24, 2022 at 11:21 AM  wrote:

>
>
> --
>
> *From: *"Ethan McCue" 
> *To: *"Remi Forax" 
> *Cc: *"John Hendrikx" , "core-libs-dev" <
> core-libs-dev@openjdk.org>
> *Sent: *Wednesday, August 24, 2022 4:27:01 PM
> *Subject: *Re: Proposal: Collection mutability marker interfaces
>
> > so it's perhaps better to call add() inside a try/catch on
> UnsupportedOperationException.
>
> As much as sin is wrong, sketching out what that might look like...
> forgive the toyness of the example
>
>
>
>
>
>
> VS
>
>
>
> final class Ex {
> private Ex() {}
>
> /*
>  * Adds the odd numbers from 1 to 10 to the List then makes all the
> odds even.
>  *
>  * Takes ownership of the list, avoids making copies if it doesn't
> need to
>  */
> static List addOdds(List l) {
> for (int i = 1; i <= 10; i++) {
> try {
> l.add(i);
> } catch (UnsupportedOperationException e) {
> l = new ArrayList<>(l);
>
>
> i -= 1;  // restart with an ArrayList
>
>
> }
> }
>
> for (int i = 0; i < l.size(); i++) {
> if (l.get(i) % 2 == 1) {
> try {
> l.set(i, l.get(i) + 1);
> } catch (UnsupportedOperationException e) {
> l = new ArrayList<>(l);
> }
> }
> }
> }
> }
>
>
> as Roger said, there is no way in Java to know if the caller has not kept
> a reference (unlike Rust),
> so having trouble to write this kind of code is more a feature than an
> issue :)
>
> This kind of examples scream the Stream API, which has the correct
> semantics
>   IntStream.rangeClosed(1, 10).map(i -> i % 2 == 0? i + 1:
> i).boxed().toList()
>
> Rémi
>
>
>
>
> On Wed, Aug 24, 2022 at 10:03 AM Remi Forax  wrote:
>
>>
>>
>> --
>>
>> *From: *"Ethan McCue" 
>> *To: *"John Hendrikx" 
>> *Cc: *"core-libs-dev" 
>> *Sent: *Wednesday, August 24, 2022 3:38:26 PM
>> *Subject: *Re: Proposal: Collection mutability marker interfaces
>>
>> A use case that doesn't cover is adding to a collection.
>>
>> Say as part of a method's contract you state that you take ownership of a
>> List. You aren't going to copy even if the list is mutable.
>>
>> Later on, you may want to add to the list. Add is supported on ArrayList
>> so you don't need to copy and replace your reference, but you would if the
>> list you were given was made with List.of or Arrays.asList
>>
>>
>> You can ask if the spliterator considers the collection as immutable or
>> not,
>>   list.spliterator().hasCharacteristics(Spliterator.IMMUTABLE)
>>
>> sadly, List.of()/Map.of() does not report the spliterator characteristics
>> correctly (the spliterator implementations are inherited from
>> AbstracList/AbstractMap).
>>
>> so it's perhaps better to call add() inside a try/catch on
>> UnsupportedOperationException.
>>
>> Rémi
>>
>>
>> On Wed, Aug 24, 2022, 8:13 AM John Hendrikx 
>> wrote:
>>
>>> Would it be an option to not make the receiver responsible for the
>>> decision whether to make a copy or not?  Instead put this burden (using
>>> default methods) on the various collections?
>>>
>>> If List/Set/Map had a method like this:
>>>
>>>  List immutableCopy();  // returns a (shallow) immutable copy if
>>> list is mutable (basically always copies, unless proven otherwise)
>>>
>>> Paired with methods on Collections to prevent collections from being
>>> modified:
>>>
>>>  Collections.immutableList(List)
>>>
>>> This wrapper is similar to `unmodifiableList` except it implements
>>> `immutableCopy` as `return this`.
>>>
>>> Then for the various scenario's, where `x` is an untrusted source of
>>> List with unknown status:
>>>
>>>  // Create a defensive copy; result is a private list that cannot be
>>> modified:
>>>
>>>  List y = x.immutableCopy();
>>>
>>>  // Create a defensive copy for sharing, pr

Re: Proposal: Collection mutability marker interfaces

2022-08-24 Thread Ethan McCue
> Safety-wise, taking transferring ownership is fraught

True, or at the very least a true-ism we generally accept.

But that's still a deliberate choice to make that makes a performance
tradeoff. The risk of misuse is proportional always to the exposure of and
audience of the api.

On Wed, Aug 24, 2022 at 10:28 AM Roger Riggs  wrote:

> Hi,
>
> Safety-wise, taking transferring ownership is fraught, the new owner can't
> be certain that the original owner hasn't kept a reference to it or to its
> implementation and might be mucking around with it behind the new owners
> back.
>
> Its cleaner to design the APIs to be defensive, either the API should
> handle the List creation itself (and only expose immutable Lists) or make
> defensive copies before using or saving it.
>
> $0.02, Roger
>
>
> On 8/24/22 9:38 AM, Ethan McCue wrote:
>
> A use case that doesn't cover is adding to a collection.
>
> Say as part of a method's contract you state that you take ownership of a
> List. You aren't going to copy even if the list is mutable.
>
> Later on, you may want to add to the list. Add is supported on ArrayList
> so you don't need to copy and replace your reference, but you would if the
> list you were given was made with List.of or Arrays.asList
>
> On Wed, Aug 24, 2022, 8:13 AM John Hendrikx 
> wrote:
>
>> Would it be an option to not make the receiver responsible for the
>> decision whether to make a copy or not?  Instead put this burden (using
>> default methods) on the various collections?
>>
>> If List/Set/Map had a method like this:
>>
>>  List immutableCopy();  // returns a (shallow) immutable copy if
>> list is mutable (basically always copies, unless proven otherwise)
>>
>> Paired with methods on Collections to prevent collections from being
>> modified:
>>
>>  Collections.immutableList(List)
>>
>> This wrapper is similar to `unmodifiableList` except it implements
>> `immutableCopy` as `return this`.
>>
>> Then for the various scenario's, where `x` is an untrusted source of List
>> with unknown status:
>>
>>  // Create a defensive copy; result is a private list that cannot be
>> modified:
>>
>>  List y = x.immutableCopy();
>>
>>  // Create a defensive copy for sharing, promising it won't ever
>> change:
>>
>>  List y = Collections.immutableList(x.immutableCopy());
>>
>>  // Create a defensive copy for mutating:
>>
>>  List y = new ArrayList<>(x);  // same as always
>>
>>  // Create a mutable copy, modify it, then expose as immutable:
>>
>>  List y = new ArrayList<>(x);  // same as always
>>
>>  y.add(  );
>>
>>  List z = Collections.immutableList(y);
>>
>>  y = null;  // we promise `z` won't change again by clearing the only
>> path to mutating it!
>>
>> The advantage would be that this information isn't part of the type
>> system where it can easily get lost. The actual implementation knows best
>> whether a copy must be made or not.
>>
>> Of course, the immutableList wrapper can be used incorrectly and the
>> promise here can be broken by keeping a reference to the original (mutable)
>> list, but I think that's an acceptable trade-off.
>>
>> --John
>>
>> PS. Chosen names are just for illustration; there is some discussion as
>> what "unmodifiable" vs "immutable" means in the context of collections that
>> may contain elements that are mutable. In this post, immutable refers to
>> shallow immutability .
>> On 24/08/2022 03:24, Ethan McCue wrote:
>>
>> Ah, I'm an idiot.
>>
>> There is still a proposal here somewhere...maybe. right now non jdk lists
>> can't participate in the special casing?
>>
>> On Tue, Aug 23, 2022, 9:00 PM Paul Sandoz  wrote:
>>
>>> List.copyOf already does what you want.
>>>
>>>
>>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068
>>>
>>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168
>>>
>>> Paul.
>>>
>>> > On Aug 23, 2022, at 4:49 PM, Ethan McCue  wrote:
>>> >
>>> > Hi all,
>>> >
>>> > I am running into an issue with the collections framework where I have
>>> to choose between good semantics for users and performance.
>>> >
>>> > Specifically I am taking a java.util.List from my users and I need to
>>> choose to either
&g

Re: Proposal: Collection mutability marker interfaces

2022-08-24 Thread Ethan McCue
> so it's perhaps better to call add() inside a try/catch on
UnsupportedOperationException.

As much as sin is wrong, sketching out what that might look like... forgive
the toyness of the example

final class Ex {
private Ex() {}

/*
 * Adds the odd numbers from 1 to 10 to the List then makes all the
odds even.
 *
 * Takes ownership of the list, avoids making copies if it doesn't need
to
 */
static List addOdds(List l) {
for (int i = 1; i <= 10; i++) {
l.add(i);
}

for (int i = 0; i < l.size(); i++) {
if (l.get(i) % 2 == 1) {
l.set(i, l.get(i) + 1);
}
}
}
}


VS



final class Ex {
private Ex() {}

/*
 * Adds the odd numbers from 1 to 10 to the List then makes all the
odds even.
 *
 * Takes ownership of the list, avoids making copies if it doesn't need
to
 */
static List addOdds(List l) {
for (int i = 1; i <= 10; i++) {
try {
l.add(i);
} catch (UnsupportedOperationException e) {
l = new ArrayList<>(l);
}
}

for (int i = 0; i < l.size(); i++) {
if (l.get(i) % 2 == 1) {
try {
l.set(i, l.get(i) + 1);
} catch (UnsupportedOperationException e) {
l = new ArrayList<>(l);
}
}
}
}
}

VS

final class Ex {
private Ex() {}

/*
 * Adds the odd numbers from 1 to 10 to the List then makes all the
odds even.
 *
 * Takes ownership of the list, avoids making copies if it doesn't need
to
 */
static List addOdds(List l) {
if (!(l instanceof Settable && l instanceof Addable)) {
l = new ArrayList<>(l);
}
for (int i = 1; i <= 10; i++) {
l.add(i);
}

for (int i = 0; i < l.size(); i++) {
if (l.get(i) % 2 == 1) {
l.set(i, l.get(i) + 1);
}
}
}
}



On Wed, Aug 24, 2022 at 10:03 AM Remi Forax  wrote:

>
>
> --
>
> *From: *"Ethan McCue" 
> *To: *"John Hendrikx" 
> *Cc: *"core-libs-dev" 
> *Sent: *Wednesday, August 24, 2022 3:38:26 PM
> *Subject: *Re: Proposal: Collection mutability marker interfaces
>
> A use case that doesn't cover is adding to a collection.
>
> Say as part of a method's contract you state that you take ownership of a
> List. You aren't going to copy even if the list is mutable.
>
> Later on, you may want to add to the list. Add is supported on ArrayList
> so you don't need to copy and replace your reference, but you would if the
> list you were given was made with List.of or Arrays.asList
>
>
> You can ask if the spliterator considers the collection as immutable or
> not,
>   list.spliterator().hasCharacteristics(Spliterator.IMMUTABLE)
>
> sadly, List.of()/Map.of() does not report the spliterator characteristics
> correctly (the spliterator implementations are inherited from
> AbstracList/AbstractMap).
>
> so it's perhaps better to call add() inside a try/catch on
> UnsupportedOperationException.
>
> Rémi
>
>
> On Wed, Aug 24, 2022, 8:13 AM John Hendrikx 
> wrote:
>
>> Would it be an option to not make the receiver responsible for the
>> decision whether to make a copy or not?  Instead put this burden (using
>> default methods) on the various collections?
>>
>> If List/Set/Map had a method like this:
>>
>>  List immutableCopy();  // returns a (shallow) immutable copy if
>> list is mutable (basically always copies, unless proven otherwise)
>>
>> Paired with methods on Collections to prevent collections from being
>> modified:
>>
>>  Collections.immutableList(List)
>>
>> This wrapper is similar to `unmodifiableList` except it implements
>> `immutableCopy` as `return this`.
>>
>> Then for the various scenario's, where `x` is an untrusted source of List
>> with unknown status:
>>
>>  // Create a defensive copy; result is a private list that cannot be
>> modified:
>>
>>  List y = x.immutableCopy();
>>
>>  // Create a defensive copy for sharing, promising it won't ever
>> change:
>>
>>  List y = Collections.immutableList(x.immutableCopy());
>>
>>  // Create a defensive copy for mutating:
>>
>>  List y = new ArrayList<>(x);  // same as always
>>
>>  // Create a mutable copy, modify it, then expose as immutable:
>>
>>  List y = new ArrayList<>(x);  // same as always
>>
>>  y.add(  );
>>
>>  List z = Colle

Re: Proposal: Collection mutability marker interfaces

2022-08-24 Thread Ethan McCue
A use case that doesn't cover is adding to a collection.

Say as part of a method's contract you state that you take ownership of a
List. You aren't going to copy even if the list is mutable.

Later on, you may want to add to the list. Add is supported on ArrayList so
you don't need to copy and replace your reference, but you would if the
list you were given was made with List.of or Arrays.asList

On Wed, Aug 24, 2022, 8:13 AM John Hendrikx  wrote:

> Would it be an option to not make the receiver responsible for the
> decision whether to make a copy or not?  Instead put this burden (using
> default methods) on the various collections?
>
> If List/Set/Map had a method like this:
>
>  List immutableCopy();  // returns a (shallow) immutable copy if
> list is mutable (basically always copies, unless proven otherwise)
>
> Paired with methods on Collections to prevent collections from being
> modified:
>
>  Collections.immutableList(List)
>
> This wrapper is similar to `unmodifiableList` except it implements
> `immutableCopy` as `return this`.
>
> Then for the various scenario's, where `x` is an untrusted source of List
> with unknown status:
>
>  // Create a defensive copy; result is a private list that cannot be
> modified:
>
>  List y = x.immutableCopy();
>
>  // Create a defensive copy for sharing, promising it won't ever
> change:
>
>  List y = Collections.immutableList(x.immutableCopy());
>
>  // Create a defensive copy for mutating:
>
>  List y = new ArrayList<>(x);  // same as always
>
>  // Create a mutable copy, modify it, then expose as immutable:
>
>  List y = new ArrayList<>(x);  // same as always
>
>  y.add(  );
>
>  List z = Collections.immutableList(y);
>
>  y = null;  // we promise `z` won't change again by clearing the only
> path to mutating it!
>
> The advantage would be that this information isn't part of the type system
> where it can easily get lost. The actual implementation knows best whether
> a copy must be made or not.
>
> Of course, the immutableList wrapper can be used incorrectly and the
> promise here can be broken by keeping a reference to the original (mutable)
> list, but I think that's an acceptable trade-off.
>
> --John
>
> PS. Chosen names are just for illustration; there is some discussion as
> what "unmodifiable" vs "immutable" means in the context of collections that
> may contain elements that are mutable. In this post, immutable refers to
> shallow immutability .
> On 24/08/2022 03:24, Ethan McCue wrote:
>
> Ah, I'm an idiot.
>
> There is still a proposal here somewhere...maybe. right now non jdk lists
> can't participate in the special casing?
>
> On Tue, Aug 23, 2022, 9:00 PM Paul Sandoz  wrote:
>
>> List.copyOf already does what you want.
>>
>>
>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068
>>
>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168
>>
>> Paul.
>>
>> > On Aug 23, 2022, at 4:49 PM, Ethan McCue  wrote:
>> >
>> > Hi all,
>> >
>> > I am running into an issue with the collections framework where I have
>> to choose between good semantics for users and performance.
>> >
>> > Specifically I am taking a java.util.List from my users and I need to
>> choose to either
>> > * Not defensively copy and expose a potential footgun when I pass that
>> List to another thread
>> > * Defensively copy and make my users pay an unnecessary runtime cost.
>> >
>> > What I would really want, in a nutshell, is for List.copyOf to be a
>> no-op when used on lists made with List.of().
>> >
>> > Below the line is a pitch I wrote up on reddit 7 months ago for a
>> mechanism I think could accomplish that. My goal is to share the idea a bit
>> more widely and to this specific audience to get feedback.
>> >
>> >
>> https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share_medium=web2x=3
>> >
>> > Important also for context is Ron Pressler's comment above.
>> > --
>> >
>> > What if the collections api added more marker interfaces like
>> RandomAccess?
>> >
>> > It's already a common thing for codebases to make explicit null checks
>> at error boundaries because the type system can't encode null |
>> List.
>> >
>> > This feels like a similar problem.
>> > If you have a List in the type system then you do

Re: Proposal: Collection mutability marker interfaces

2022-08-23 Thread Ethan McCue
Ah, I'm an idiot.

There is still a proposal here somewhere...maybe. right now non jdk lists
can't participate in the special casing?

On Tue, Aug 23, 2022, 9:00 PM Paul Sandoz  wrote:

> List.copyOf already does what you want.
>
>
> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068
>
> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168
>
> Paul.
>
> > On Aug 23, 2022, at 4:49 PM, Ethan McCue  wrote:
> >
> > Hi all,
> >
> > I am running into an issue with the collections framework where I have
> to choose between good semantics for users and performance.
> >
> > Specifically I am taking a java.util.List from my users and I need to
> choose to either
> > * Not defensively copy and expose a potential footgun when I pass that
> List to another thread
> > * Defensively copy and make my users pay an unnecessary runtime cost.
> >
> > What I would really want, in a nutshell, is for List.copyOf to be a
> no-op when used on lists made with List.of().
> >
> > Below the line is a pitch I wrote up on reddit 7 months ago for a
> mechanism I think could accomplish that. My goal is to share the idea a bit
> more widely and to this specific audience to get feedback.
> >
> >
> https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share_medium=web2x=3
> >
> > Important also for context is Ron Pressler's comment above.
> > --
> >
> > What if the collections api added more marker interfaces like
> RandomAccess?
> >
> > It's already a common thing for codebases to make explicit null checks
> at error boundaries because the type system can't encode null |
> List.
> >
> > This feels like a similar problem.
> > If you have a List in the type system then you don't know for sure
> you can call any methods on it until you check that its not null. In the
> same way, there is a set of methods that you don't know at the
> type/interface level if you are allowed to call.
> >
> > If the List is actually a __
> > Then you can definitely call
> > And you know other reference holders might call
> > And you can confirm its this case by
> > null
> > no methods
> > no methods
> > list == null
> > List.of(...)
> > get, size
> > get, size
> > ???
> > Collections.unmodifiableList(...)
> > get, size
> > get, size, add, set
> > ???
> > Arrays.asList(...)
> > get, size, set
> > get, size, set
> > ???
> > new ArrayList<>()
> > get, size, add, set
> > get, size, add, set
> > ???
> > While yes, there is no feasible way to encode these things in the type
> system. Its not impossible to encode it at runtime though.
> > interface FullyImmutable {
> > // So you know the existence of this implies the absence
> > // of the others
> > default Void cantIntersect() { return null; }
> > }
> >
> > interace MutationCapability {
> > default String cantIntersect() { return ""; }
> > }
> >
> > interface Addable extends MutationCapability {}
> > interface Settable extends MutationCapability {}
> >
> > If the List is actually a __
> > Then you can definitely call
> > And you know other reference holders might call
> > And you can confirm its this case by
> > null
> > no methods
> > no methods
> > list == null
> > List.of(...)
> > get, size
> > get, size
> > instanceof FullyImmutable
> > Collections.unmodifiableList(...)
> > get, size
> > get, size, add, set
> > !(instanceof Addable) && !(instanceof Settable)
> > Arrays.asList(...)
> > get, size, set
> > get, size, set
> > instanceof Settable
> > new ArrayList<>()
> > get, size, add, set
> > get, size, add, set
> > instanceof Settable && instanceof Addable
> > In the same way a RandomAccess check let's implementations decide
> whether they want to try an alternative algorithm or crash, some marker
> "capability" interfaces would let users of a collection decide if they want
> to clone what they are given before working on it.
> >
> >
> > --
> >
> > So the applicability of this would be that the list returned by List.of
> could implement FullyImmutable, signifying that there is no caller which
> might have a mutable handle on the collection. Then List.of could check for
> this interface and skip a copy.
> >
> >
>
>


Proposal: Collection mutability marker interfaces

2022-08-23 Thread Ethan McCue
Hi all,

I am running into an issue with the collections framework where I have to
choose between good semantics for users and performance.

Specifically I am taking a java.util.List from my users and I need to
choose to either
* Not defensively copy and expose a potential footgun when I pass that List
to another thread
* Defensively copy and make my users pay an unnecessary runtime cost.

What I would really want, in a nutshell, is for List.copyOf to be a no-op
when used on lists made with List.of().

Below the line is a pitch I wrote up on reddit 7 months ago for a mechanism
I think could accomplish that. My goal is to share the idea a bit more
widely and to this specific audience to get feedback.

https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share_medium=web2x=3


Important also for context is Ron Pressler's comment above.
--

What if the collections api added more marker interfaces like RandomAccess?

It's already a common thing for codebases to make explicit null checks at
error boundaries because the type system can't encode null | List.

This feels like a similar problem.
If you have a List in the type system then you don't know for sure you
can call any methods on it until you check that its not null. In the same
way, there is a set of methods that you don't know at the type/interface
level if you are allowed to call.

If the List is actually a __
Then you can definitely call
And you know other reference holders might call
And you can confirm its this case by
null
no methods
no methods
list == null
List.of(...)
get, size
get, size
???
Collections.unmodifiableList(...)
get, size
get, size, add, set
???
Arrays.asList(...)
get, size, set
get, size, set
???
new ArrayList<>()
get, size, add, set
get, size, add, set
???
While yes, there is no feasible way to encode these things in the type
system. Its not impossible to encode it at runtime though.
interface FullyImmutable {
// So you know the existence of this implies the absence
// of the others
default Void cantIntersect() { return null; }
}

interace MutationCapability {
default String cantIntersect() { return ""; }
}

interface Addable extends MutationCapability {}
interface Settable extends MutationCapability {}

If the List is actually a __
Then you can definitely call
And you know other reference holders might call
And you can confirm its this case by
null
no methods
no methods
list == null
List.of(...)
get, size
get, size
instanceof FullyImmutable
Collections.unmodifiableList(...)
get, size
get, size, add, set
!(instanceof Addable) && !(instanceof Settable)
Arrays.asList(...)
get, size, set
get, size, set
instanceof Settable
new ArrayList<>()
get, size, add, set
get, size, add, set
instanceof Settable && instanceof Addable
In the same way a RandomAccess check let's implementations decide whether
they want to try an alternative algorithm or crash, some marker
"capability" interfaces would let users of a collection decide if they want
to clone what they are given before working on it.


--

So the applicability of this would be that the list returned by List.of
could implement FullyImmutable, signifying that there is no caller which
might have a mutable handle on the collection. Then List.of could check for
this interface and skip a copy.