--
guava-...@googlegroups.com.
http://groups.google.com/group/guava-discuss?hl=en
unsubscribe: guava-discus...@googlegroups.com
This list is for discussion; for help, post to Stack Overflow instead:
http://stackoverflow.com/questions/ask
Use the tag "guava".
To unsubscribe from this group, send email to guava-discuss+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
Perhaps the jar should be complete, but an annotation processor can warn or fail when unstable APIs are used? If this is workable, I'd happily switch from javadoc tagging to annotations in our source files.
--
I don't understand why frozen and unfrozen APIs are in the same jar. API under development is unfrozen by definition and can be released under different version and library developers using that are aware that they are using unfrozen API. Other application developers can use the released STABLE version until the new API becomes stable again.
I wonder if it's possible to use jarjar style tricks during the build-process? I assume that the stable classes do not reference any of the unstable classes (otherwise they're transitively unstable!)
Consider the following thought experiment:We have the following jarsguava-core-1.0.jarguava-unstable_r100-1.0.jarguava-unstable_r200-1.0.jarNote that the release is part of the jar name, it is not part of the version numbering. This means that ivy/maven can include both in a project. In both cases guava-ustable_r100/r200 depend on the stable library.
Android does something similar. They hide experimental APIs. Then, they generate non-functional stub classes that have the hidden parts removed (all methods throw UnsupportedOperationException, so they compile w/ no problem). You compile against the stubs and run against the real thing. If you want to use an experimental API, compile against the real thing. It would be nice to see better tool support for this sort of setup.
--
When you first add a tentative method, declare it as:
public boolean _methodName_v1(String param);
Later you change your mind and simply @Deprecate that first one and
add:
public boolean _methodName_v2(String param, Predicate<Foo>
predicate);
If possible, adapt v1 to call v2. Perhaps in this case the
implementation moves to v2, modified of course to accept the
Predicate, and v1 becomes:
@Deprecated
public boolean _methodName_v1(String param) {
return _methodName_v2(param, Predicates.<Foo>alwaysTrue());
}
When one day it all settles down you move the implementation to
methodName(String, Predicate), and @Deprecate _v2 and have it call the
final implementation.
You could include an annotation processor for application developers
that removes the @Deprecated methods, or based on another annotation
that could also be on the versioned methods.
Classes are more difficult. SomeClass extends _SomeClass_v2 extends
_SomeClass_v1. There's not really a good way to strip the cruft for an
application developer, but this could be crafted to keep binary
compatibility for library users.
This is a total mess, but I think it could work.
Using osgi pretty much solves those dependency problems. Which is, not
surprisingly, why most projects use osgi as soon as they hit a certain
size. (and that is sooner than you might think)
Again, I'm really sorry if this is not helpful, but all of the
proposed solutions will have a painful impact on your productivity
(and perhaps worse, possibly also on the productivity of your library
users).
Johan Van den Neste
--
Perhaps not terribly helpful, but this is why osgi should have been
built into the jdk from the start.
Using osgi pretty much solves those dependency problems. Which is, not
surprisingly, why most projects use osgi as soon as they hit a certain
size. (and that is sooner than you might think)
Again, I'm really sorry if this is not helpful, but all of the
proposed solutions will have a painful impact on your productivity
(and perhaps worse, possibly also on the productivity of your library
users).
--
guava-...@googlegroups.com.
http://groups.google.com/group/guava-discuss?hl=en
unsubscribe: guava-discus...@googlegroups.com
This list is for discussion; for help, post to Stack Overflow instead:
http://stackoverflow.com/questions/ask
Use the tag "guava".
To unsubscribe from this group, send email to guava-discuss+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
If putting annotations in there makes you happy, go for it, but I'd recommend that you _definitely_ keep the Javadoc tags in there; anyone can look through those even if they don't speak annotations.
@Frozen vs. @Unfrozen?@ApiFrozen vs. @ApiUnfrozen?@ApiFrozen(true) vs. @ApiFrozen(false)?@ApiState(ApiStates.FROZEN) vs. @ApiState(ApiStates.UNFROZEN) (ok, yuck)
@Frozen vs. @Unfrozen?@ApiFrozen vs. @ApiUnfrozen?@ApiFrozen(true) vs. @ApiFrozen(false)?@ApiState(ApiStates.FROZEN) vs. @ApiState(ApiStates.UNFROZEN) (ok, yuck)@Published(since="1.0") vs. @Experimental
"experimental" would be the default, so we could do without @Experimental entirely. Thoughts?
I'm curious why "since <version>" is useful information. Let's say that we created foo(Bar) in version 2010.05.01, then changed it to foo(Baz) in version 2010.06.01, then marked it as frozen/published/whatever in version 2010.07.01. From that point on, the only version number that is of any interest is the middle one -- 2010.06.01. That is the minimum version of the library that one needs to have in order to use the method. The fact that 2010.07.01 is when the stable badge was applied is kind of meaningless information, is it not?
@Frozen vs. @Unfrozen?@ApiFrozen vs. @ApiUnfrozen?
@ApiFrozen(true) vs. @ApiFrozen(false)?@ApiState(ApiStates.FROZEN) vs. @ApiState(ApiStates.UNFROZEN) (ok, yuck)
"experimental" would be the default, so we could do without @Experimental entirely. Thoughts?Again, we need both to be explicit. Picture a class like Maps.java having 50 frozen methods and one unfrozen/experimental one. If we only had @Published, we'd have to put it on all 49 of the others. With both, either can override the other.
--
guava-...@googlegroups.com.
http://groups.google.com/group/guava-discuss?hl=en
unsubscribe: guava-discus...@googlegroups.com
This list is for discussion; for help, post to Stack Overflow instead:
http://stackoverflow.com/questions/ask
Use the tag "guava".
To unsubscribe from this group, send email to guava-discuss+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
Joshua,It seems like you're looking at it completely from the perspective of applications using the API, where the user can just change their code to accommodate API changes in new versions.
Kevin already said that isn't really an issue.The issue is when other libraries or frameworks depend on Guava and applications depend on those. Let's say there's a Guava 1.0 with unfrozen parts that could be changed in the future. Let's say we have an application that uses Guava and two other libraries that themselves also use Guava. Now let's say a Guava 2.0 is released, with breaking changes in the unfrozen parts of the API. If either of the other libraries using Guava use anything from 1.0 that was incompatibly changed in 2.0, we can neither upgrade to Guava 2.0 in our application nor upgrade either library to a new version that uses 2.0 while the other does not have a version out that supports it.
I'm curious why "since <version>" is useful information. Let's say that we created foo(Bar) in version 2010.05.01, then changed it to foo(Baz) in version 2010.06.01, then marked it as frozen/published/whatever in version 2010.07.01. From that point on, the only version number that is of any interest is the middle one -- 2010.06.01. That is the minimum version of the library that one needs to have in order to use the method. The fact that 2010.07.01 is when the stable badge was applied is kind of meaningless information, is it not?
On the terms published vs. experimental -- meh. I feel like people would have to dig in further to what exactly that means, and we'd finally end up speaking in terms of whether the API is frozen or not. I feel like API "frozenness" is the most precise description of what we're talking about here that I've heard.
Again, we need both to be explicit. Picture a class like Maps.java having 50 frozen methods and one unfrozen/experimental one. If we only had @Published, we'd have to put it on all 49 of the others. With both, either can override the other.
I still don't understand why @Published/@Frozen is necessary. How useful/likely is the opposite case: a class with 50 experimental methods and 1 published method? Not very, right?So the real default is frozen -- that's what we assume about an interface that makes no claims currently, right?In which case, how about just @Experimental (on class or method) and not @Published/@Frozen/etc.? I like @Experimental -- doesn't seem too strong at all.
I don't like "Unfrozen" because that implies that it was once frozen. How about "Liquid"? "Fluid"? :)
Have both annotations lets you run a test to make sure that every method/class/whatever has one of the two annotations. I wouldn't trust myself not to forget to add frozen.
On Thu, Mar 25, 2010 at 11:41 AM, Nikolas Everett <nik...@gmail.com> wrote:Have both annotations lets you run a test to make sure that every method/class/whatever has one of the two annotations. I wouldn't trust myself not to forget to add frozen.I almost made that point myself, except that we don't actually want to annotate every single method in a class like Sets or Maps -- under the two-annotation model, it would be essential that an unannotated method would inherit the annotation of its containing element. So no checker is possible in that model either.
These are valid points. We would need to be awfully careful not to forget to add the annotation when creating new experimental methods, though. I can see us forgetting this more than once. However, this is already an issue anyway if the class we're adding to is marked @Frozen.
I was predisposed against a "assume everything's fine unless we specifically say otherwise" model, thinking it would be safer to say "assume nothing unless we explicitly say so". But in reality, developers are by and large going to do the former anyway; it's the way we are.
On Thu, Mar 25, 2010 at 11:14 AM, Joshua O'Madadhain <jr...@google.com> wrote:I don't like "Unfrozen" because that implies that it was once frozen. How about "Liquid"? "Fluid"? :)@NotFrozen, then. But it doesn't work so well when not paired with @Frozen. I still feel like @Experimental overstates the case.
These are valid points. We would need to be awfully careful not to forget to add the annotation when creating new experimental methods, though. I can see us forgetting this more than once. However, this is already an issue anyway if the class we're adding to is marked @Frozen.
To those that are saying that both are necessary: remember that the _worst_ case involves someone failing to realize that something _isn't_ frozen. If we had (only) a 'frozen' annotation and we forgot to use it, then someone might grumble that they would use that method if only they were sure it would still be there later, oh well, wait for it. That's not a big problem in my book.
But here's another problem with the opposite approach -- if we have only @RiskyStayAway, it's pretty unreasonable to ask the user of Foo.bar() to check for this both on the method and on the class.
But here's another problem with the opposite approach -- if we have only @RiskyStayAway, it's pretty unreasonable to ask the user of Foo.bar() to check for this both on the method and on the class. So, putting the annotation on classes would be useless. We'd always have to add it to eeeach and eeeevery single method. Ugh again!
On Thu, Mar 25, 2010 at 11:52 AM, Joshua O'Madadhain <jr...@google.com> wrote:To those that are saying that both are necessary: remember that the _worst_ case involves someone failing to realize that something _isn't_ frozen. If we had (only) a 'frozen' annotation and we forgot to use it, then someone might grumble that they would use that method if only they were sure it would still be there later, oh well, wait for it. That's not a big problem in my book.This was my first instinct at the suggestion of having only one. However, it does mean that to add one new experimental method to a formerly-frozen class, we've got to add @Frozen to aallllll the existing methods of the class. Ugh.
But here's another problem with the opposite approach -- if we have only @RiskyStayAway, it's pretty unreasonable to ask the user of Foo.bar() to check for this both on the method and on the class. So, putting the annotation on classes would be useless. We'd always have to add it to eeeach and eeeevery single method. Ugh again!
One good thing about the two-annotation model is that we're never stuck adding dozens of annotations to a file. There is no default.
If the method doesn't tell you, the class will. There will always be something in the file you can ctrl-click or whatever to see a fuller explanation of what this freeze status business is all about.
--
Kevin Bourrillion @ Google
internal: http://goto/javalibraries
external: http://guava-libraries.googlecode.com
--
guava-...@googlegroups.com.
http://groups.google.com/group/guava-discuss?hl=en
unsubscribe: guava-discus...@googlegroups.com
This list is for discussion; for help, post to Stack Overflow instead:
http://stackoverflow.com/questions/ask
Use the tag "guava".
To unsubscribe from this group, send email to guava-discuss+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
I'm curious why "since <version>" is useful information. Let's say that we created foo(Bar) in version 2010.05.01, then changed it to foo(Baz) in version 2010.06.01, then marked it as frozen/published/whatever in version 2010.07.01. From that point on, the only version number that is of any interest is the middle one -- 2010.06.01. That is the minimum version of the library that one needs to have in order to use the method. The fact that 2010.07.01 is when the stable badge was applied is kind of meaningless information, is it not?
Also, I think it would be much better to have ordinals for versions instead of dates.
Given a version like 2010.06.01, how would a checker (or developer for that matter) easily determine the next version, or the previous version, or identify that an invalid version number had been applied? Ordinals are more useful in this regard,
Yet you see ordinal version numbers used so very rarely. I can't think of many examples. Are these benefits really important, I wonder?
These are valid points. We would need to be awfully careful not to forget to add the annotation when creating new experimental methods, though. I can see us forgetting this more than once. However, this is already an issue anyway if the class we're adding to is marked @Frozen.I was predisposed against a "assume everything's fine unless we specifically say otherwise" model, thinking it would be safer to say "assume nothing unless we explicitly say so". But in reality, developers are by and large going to do the former anyway; it's the way we are.I don't like "Unfrozen" because that implies that it was once frozen. How about "Liquid"? "Fluid"? :)@NotFrozen, then. But it doesn't work so well when not paired with @Frozen. I still feel like @Experimental overstates the case.
If that's a good way to describe your intent you might not find a better
adjective than the one you used...
@Pending
@ApiState(ApiStates.PENDING) ?
~ Graham
I don't like the idea of having two annotations (@Frozen and
@Unfrozen, for example) and having "method-level annotation overriding
the class-level annotation". While this sounds very flexible on paper,
the developer would be forced to always remember the "context" he is
in when stumbling on an unannotated method ("context" meaning whether
the class holding the method he wants to use is frozen or not):
** Developer thinking: "Maps.newGodlyMap(), this method sounds really
cool! But wait, is it tentative? Hmmm it's not annotated with either
@Frozen or @Unfrozen. Was the Maps class frozen or not? Mmh let's
check. *scrolls up* Oh yeah, the map class is frozen, so this means
this method is frozen too. Cool, let's use it then!" **
When coding, we already have to juggle with many abstractions and
contexts, let's not add another one.
Some question to ask ourselves before using @Frozen/@Unfrozen and such
an overriding mechanism:
- would it make sense "semantically" to have an @Unfrozen class with
@Frozen methods inside? If the class itself is unfrozen, how could a
method inside it be frozen?
- let's say you have a com.google.common.collect.Maps class with both
frozen and unfrozen static utility methods. Should you annotate the
class with @Frozen or @Unfrozen?
I think we should have frozen classes / methods by default, and we
should draw attention to unfrozen methods with an annotation.
You may forget to add the @Tentative annotation on new methods, but
the build/tools should tell you so in this case.
On Mar 25, 9:30 pm, Kevin Bourrillion <kev...@google.com> wrote:
> As well,
> there's another reason to favor it: years from now, we all hope that the
> frozen portion of the library will be much larger than the unfrozen portion.
> So it makes sense to consider the unfrozen parts as the ones worth flagging
> as being the exceptions to the norm.
Very good point. In the end, when most of the API is frozen, having
@Frozen annotations all over the code would be just clutter.
Readability is important, and limiting annotations would help
readability.
--------------------------------------------------------
*** On the @Frozen/@Unfrozen versus @Frozen(true)/@Frozen(false)
debate ***
If we still choose to go with multiple annotations, I don't like the
idea of having something like @Frozen(true) / @Frozen(false) or
@ApiState(States.Frozen) / ApiState(States.UnFrozen).
Yes, it would be more flexible, and it would stop us from accidentally
annotating the same method with both @Frozen and @Unfrozen.
But:
- it's less readable: @Unfrozen is shorter/cooler than @Frozen(false)
- using such language tricks is very useful and elegant, especially
when it stops *your API users* from making mistakes. But in this case,
your users will only "read" these annotations (1). You are the ones
who control the Guava code and annotate methods, and you will not make
these mistakes because...
- your build may be configured to alert you when you annotate a member
is annotated with both annotations
(1) - Unless the plan is to create @Frozen/@Unfrozen annotation which
will then be used by other library developers in their own APIs? But I
think the goal is mainly to document the guava code, not to create a
"versioning framework".
--------------------------------------------------------
*** On versioning ***
Instead of using dates/numbers, why not add an enum somewhere, such
as:
public enum Version {
/**
* Version 1.0
* Added 2009-03-31.
*/
1_0,
/**
* Version 1.1
* Added 2009-04-22.
*/
1_1,
/**
* Version 1.5.
* Added 2009-05-13.
*/
1_5;
}
Then in your code:
@Since(Version.1_1)
Advantages:
- user will see useful informations in the javadoc (by hovering over
the version enum). Of course, you shouldn't add too much information
there if you don't want the enum to become really big, but some basic
info about the version, as well as a link to the release notes, would
be great. Modern IDEs (at least Eclipse) show the javadoc on hovering.
- you can very simply find all methods added in a specific version
(ctrl + alt + H in Eclipse - find usages)
- type safe: no typo allowed
Disadvantages:
- you may not use dots in a java variable, so you have to write 1_0 /
1_2_31 instead of 1.0 / 1.2.31
From first principals, what is the real goal of Guava? I've been a
user of Google-Collections for a long time, and love them, and if
there were more depth and breath to this new Guava thing, that sounds
cool to me.
But the complexity that Kevin mentions from the start of this thread
is real. Its real in any software project. And there are billions of
lines of code and billions of dollars spent on projects that flat out
fail because of their complexity.
Perhaps we need, instead of some trick with annotations, to use a more
fundamental part of "agile development" and not try to make the
greatest most google-teriffic library in the universe. Partition the
problem, with clearly defined modules and clearly defined internal
walls. If the Google Matrix Math library has to rely upon a specific
version of the Google-Collections library, is it really all that bad
to bind them closely and internally?
On 25 bře, 19:13, Tim Peierls <t...@peierls.net> wrote:
> On Thu, Mar 25, 2010 at 1:58 PM, Kevin Bourrillion <kev...@google.com>wrote:
>
> > "experimental" would be the default, so we could do without @Experimental
> >> entirely. Thoughts?
>
> > Again, we need both to be explicit. Picture a class like Maps.java having
> > 50 frozen methods and one unfrozen/experimental one. If we only had
> > @Published, we'd have to put it on all 49 of the others. With both, either
> > can override the other.
>
> I still don't understand why @Published/@Frozen is necessary. How
> useful/likely is the opposite case: a class with 50 experimental methods and
> 1 published method? Not very, right?
>
> So the real default is frozen -- that's what we assume about an interface
> that makes no claims currently, right?
>
> In which case, how about just @Experimental (on class or method) and not
> @Published/@Frozen/etc.? I like @Experimental -- doesn't seem too strong at
> all.
>
> --tim
I agree with the annotation approach, but only one for @Beta (or
whatever you name it). As the library becomes more stable, I would
rather see fewer annotations than @Frozen on every class.
As an alternative/additional approach, you should seriously consider
putting experimental code in a separate package. I'm in the process of
rewriting one of my projects, and using different packages is
definitely the cleanest (simple) way to keep users of the old and new
libraries from being incompatible. Especially since both need to live
in the same class loader. But that doesn't help too much with the "one
experimental method on Maps" problem.
Most of you are thinking about the problems you'll encounter using
Guava directly (since that's who's on this forum), but you control the
Guava-calling code. So it's not really a huge problem. A much tougher
problem would be if your project depended on both
1) Widget, which depends (possibly transitively) on Guava 1.0
2) Gizmo, which depends (possibly transitively) on Guava 2.0
and both Widget and Gizmo use parts of Guava that are API-incompatible
with each other. The user has very few options here, other than
abandoning or forking one of their dependencies.
Now, multiply that by a few more dependencies, since Guava is a very
general library and could be used by many other projects.
Using the same package bit me recently. We were on Java 1.5 and
(transitively) using a 3rd party implementation of javax.xml.bind.
Then we moved to Java 1.6 and everything broke, because the 3rd party
implementation was API-incompatible with the classes/interfaces in the
new JDK. IMHO, Sun made a huge mistake allowing 3rd parties use of the
javax package namespace. We've had the same problem previously with
org.w3c.dom implementations.
- Ray A. Conner
I agree with the annotation approach, but only one for @Beta (or
whatever you name it).
As an alternative/additional approach, you should seriously consider
putting experimental code in a separate package.
I'm in the process of
rewriting one of my projects, and using different packages is
definitely the cleanest (simple) way to keep users of the old and new
libraries from being incompatible.
Most of you are thinking about the problems you'll encounter using
Guava directly (since that's who's on this forum), but you control the
Guava-calling code. So it's not really a huge problem. A much tougher
problem would be if your project depended on both
On Fri, Mar 26, 2010 at 10:39 AM, Ray Conner <ray.a....@gmail.com> wrote:As an alternative/additional approach, you should seriously consider
putting experimental code in a separate package.We do this internally, with packages called com.google.common.labs.*. It's hell. All the users have to change when things graduate, and even worse, we can't access other parts of the library that are package-private, so things have to be made public that shouldn't be. I'm not eager to replicate this practice further!
I'm in the process of
rewriting one of my projects, and using different packages is
definitely the cleanest (simple) way to keep users of the old and new
libraries from being incompatible.So this assumes that each time an incompatible change is made, you're always changing the package. I don't see this working out.
--
Kevin Bourrillion @ Google
internal: http://goto/javalibraries
external: http://guava-libraries.googlecode.com
--