On Sun, Feb 15, 2009 at 7:13 PM, Reinier Zwitserloot <[email protected]>wrote:
> > I mark any immutable class final, because if it isn't, then you can't > rely on its immutability (any subclass is assignment compatible and is > not neccessarily immutable!). While I tend to agree with marking "type" classes as final, I don't believe that a class being immutable is reason enough to mark it final. It is just a way to constraint anything on the class to suite a single concern. It is probably true that an immutable Day class with a couple of method (including add(Day): Day) could be made final without being too much of a pain later. Though, taking that example of the write(OutputStream) method, if know all a sudden I want to make the writing to that stream "monitorable" for progress for instance, chances are high I will have to override the method to do a bit more than the initial implementation does. And if that gives me a chance to break any contractthat the class provided before (like being immutable), well... though luck! We shouldn't consider every user of our api's to be complete idiots and hence constraining them as much as possible. We should rather strive to make the contract that a particular class implements clear and well documented, enabling those classes to live their live in the ecosystem safetly... My $.02 Alex > > > We can call it immutable instead of side-effect-free, but the key > property that an automated tool checks for must be side-effects of any > kind. It isn't possible to determine if a method that causes a side- > effect on anything makes the object that it belongs to conceptually > mutable. Simply impossible. In fact, it gets more complicated. Imagine > a simple struct-like class that has only final primitive fields. One > of its utility methods is to save itself to disk via a supplied file > name (or, more properly, by writing itself to a supplied > OutputStream). Clearly this object is immutable, and everything except > the write operation could be memoized. But if it also had a method > that read a file from disk and then returned something that was in > there, that particular method is definitely not memoizable. So, there > are rather distinct use cases: > > - safe to pass around. (String, immutable Lists/Sets/Maps containing > only immutable keys and values). > - not really safe to pass around, but cloning it won't help. > (java.io.File) > - method is memoizable. (Most of string's stuff). > - all the above conditions are compiler-checked and if you add code > to breaks the effect, you get an error. > > I don't think littering @SEF, @Immutable, and @Memoizable around is > the way to go. We need something simpler than that, but I can't figure > it out. > > On Feb 15, 7:28 am, Alexander Snaps <[email protected]> wrote: > > I don't think a class should be marked final except if there is good > > reason for doing so... > > As for the rest I usually don't see much value in doing it. Though if > > the project adopts it as a coding standard, why not. I'll just code > > consistent with the style. Though it doesn't add much value IMHO. > > Alex > > > > On 2/15/09, Reinier Zwitserloot <[email protected]> wrote: > > > > > > > > > > > > > And when we need to make another modification, we.... rename the > > > parameter to 'reallyOldConfigKey'? > > > > > Peter: Me too, I spread final all over the place. on classes, on > > > attributes, on methods. > > > > > The only place where I rarely use it is for method locals :P > > > > > While immutability would be great, what's really the key modifier is > > > 'Side-Effect-Free'. Immutability is not well defined in practice. SEF > > > is mostly well defined in practice (with exceptions where you really > > > want to force it for e.g. logging, which seems like a side-effect but > > > ought to be considered SEF). For example: > > > > > Your class has all-final fields, and each field's type is neccessarily > > > immutable (primitive, or a final class that is itself immutable). > > > There is a static map (irrelevant for object's mutable/immutable > > > state) that binds instances of this class to a String. The object uses > > > this to sneakily be fake mutable. In fact, is an instance of this > > > class an immutable in the first place? > > > > > What about something like java.io.File, which is immutable (final and > > > all its internal state never changes), but you can call things that > > > mutate stuff on it, stuff that is generally considered to be part of > > > the state of files, such as deleting them, creating them, etcetera. > > > > > Side-Effect-Free avoids these hairy issues. You do need ways to say: I > > > know this LOOKS like a side-effect, but consider it SEF. For example, > > > logging. It would be completely unusable if the act of logging forces > > > you to declare a method call SEF. > > > > > You can also define an object as utterly safe and entirely memoizable > > > if it contains only SEF methods and all its fields are final, and of > > > safe types. This is a very useful distinction. > > > > > APT processing might actually allow you to get somewhere with this, > > > with annotations that allow you to specify intent (e.g.: This method > > > is supposed to be SEF, so if it isn't, warn or error), and some > > > serious class introspection. It would definitely help if you cached > > > the SEF state of core java library calls (e.g. cache that after some > > > analysis, you determine that calling string.toLowerCase() is > > > harmless). > > > > > On Feb 14, 7:43 pm, Robert Fischer <[email protected]> > > > wrote: > > >> You're right -- I don't like the academic discussion of "final", > because > > >> it's in practicalities > > >> where "final" really shows its power. > > > > >> Comments intermingled below. > > > > >> Reinier Zwitserloot wrote: > > >> > If we allow modification in the pre-amble, this is easy, bordering > on > > >> > the trivial (lines //V2 have been added/modified in the update): > > > > >> > public List<ConfigDirectives> readConfig(String configKey) { > > >> > configKey = translateOldKeyNames(configKey); //V2 > > > > >> > try { > > >> > Logger.get(this.getClas()).log("Request for config file: " + > > >> > configKey); > > >> > return readConfigDirectivesFromDisk(new FileInputStream > > >> > ("configfiles/" + configKey + ".cfg")); > > >> > } catch ( FileNotFoundException e ) { > > >> > return Collections.emptyList(); > > >> > } > > >> > } > > > > >> > The one doing the editing really could get away with not even > looking > > >> > at what the actual body of the method does. Nice. > > > > >> > Now lets try with your rule: > > > > >> > Urgh. We can't. > > > > >> Really? What about this? > > > > >> public List<ConfigDirectives> readConfig(final String oldConfigKey) { > > >> final String configKey = translateOldKeyNames(configKey); //V2.1 > > > > >> try { > > >> Logger.get(this.getClas()).log("Request for config file: " + > > >> configKey); > > >> return readConfigDirectivesFromDisk(new FileInputStream > > >> ("configfiles/" + configKey + ".cfg")); > > >> } catch ( FileNotFoundException e ) { > > >> return Collections.emptyList(); > > >> } > > > > >> } > > > > >> That's a simple, easily makable, and readable change. In fact, now > I've > > >> got the original version > > >> still kicking around (in case I want it later), and I've got the > > >> translated version -- which is > > >> really semantically different than "configKey" in the previous version > -- > > >> automatically being used. > > > > >> I'm pretty sure that change meets all your criteria. > > > > >> > The optimal way to solve this one in a functional view of the world > is > > >> > to first create a new variable (keyNormalized or something), and > then > > >> > UNDEFINE the old one. This way anyone that tries to use the original > > >> > gets an error which should quickly lead to him or her coming up with > > >> > the right solution (either use the normalized version or create a > new > > >> > paramRaw variable to make it abundantly clear what's happening). > > >> > However, java doesn't support that. > > > > >> I just did it. Now, I didn't undefine the old variable, but I also > don't > > >> see how undefining the > > >> variable is a part of the "the optimal way to solve this one in a > > >> functional view of the world". > > >> Sure, it's common to build up structures like: > > > > >> let foo = "f" in > > >> let foo = foo + "o" in > > >> let foo = foo + "o" in ... > > > > >> But that's more a hack around immutability than part of the functional > > >> view of the world. Insofar > > >> as that code can be conceived of as functional, this would be just as > > >> functional: > > > > >> let f = "f" in > > >> let fo = f + "o" in > > >> let foo = fo + "o" in ... > > > > >> > Hence, my theory is: In anything but the most trivial of examples, > > >> > inlining the transformation as you did in your myConcat method, is a > > >> > bad thing more often than it being a good thing. Yes, there was a > typo > > >> > in your code (double 'left'), but I found that utterly unconvincing, > > >> > because this typo is exactly as likely in your 'correct' method: > > > > >> > String myConcat(final String left, final String right) { > > >> > if ( left == null ) return myConcat("", left); > > >> > if ( right == null ) return myConcat(left, ""); > > >> > return left + right; > > >> > } > > > > >> We're now back into the academic, but since we're arguing what types > of > > >> errors are more likely, I'd > > >> argue that an alarm bell is more likely to ring when you're putting a > > >> variable named "left" into the > > >> right side of an argument list as opposed to accidentally assigning > the > > >> wrong variable. So this > > >> error case is less likely than the other. > > > > >> Even if it's "just as likely", you're still not demonstrating the > grievous > > >> harm (or whatever) that > > >> "final" apparently does. > > > > >> ~~ Robert Fischer. > > >> Grails Training http://GroovyMag.com/training > > >> Smokejumper Consultinghttp://SmokejumperIT.com > > >> Enfranchised Mind Bloghttp://EnfranchisedMind.com/blog > > > > >> Check out my book, "Grails Persistence with GORM and > > >> GSQL"!http://www.smokejumperit.com/redirect.html > > > > -- > > Alexander Snaps <[email protected]> > http://www.jroller.com/page/greenhornhttp://www.linkedin.com/in/alexandersnaps > > > -- Alexander Snaps <[email protected]> http://www.jroller.com/page/greenhorn http://www.linkedin.com/in/alexandersnaps --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "The Java Posse" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/javaposse?hl=en -~----------~----~----~----~------~----~------~--~---
