Sounds like we're saying the same thing, no?  When I was talking about thread 
safety, I didn't mean to imply that the low-level synchronization Object API 
affect immutability of an object.  I was referring to the idea that File state, 
such as exists() is rather low-level, such that it should be stuffed down into 
implementation details of one's architecture.  In Java, it feels most natural 
to 
not force a square peg into a round hole and instead put that kind of logic 
(persistence, crazy mixed up API like Date/Calendar, etc.) behind the veneer of 
functional and immutable style so as to expose state to the rest of your system 
or client code in the way of your choosing, best suited to the chosen 
architecture.

 Alexey





________________________________
From: Kevin Wright <[email protected]>
To: [email protected]
Cc: Alexey Zinger <[email protected]>
Sent: Mon, March 14, 2011 6:04:30 AM
Subject: Re: [The Java Posse] Some design questions (about immutability and 
other stuff)


What a tangled web we weave...

There's an awful lot going on here, possibly best to get the fluff out of the 
way first to clear the "path" :)

First; wait, notify, notifyAll don't matter in the slightest.  Conceptually, 
these can be treated as though they didn't even exist, just pretend that 
they're 
static methods on some other class that take an object instance as their first 
argument.  An immutable object has no business dealing with locks, semaphores 
and other synchronisation constructructs, and if another piece of code happens 
to wait on some immutable instance `foo`, then it really doesn't affect `foo`s 
internal behaviour whatsoever, a total red herring.

More disturbingly, final isn't actually immutable! A final member can still be 
modified via reflection, native code or some form of bytecode manipulation.  So 
if you're desparate to find ways that JVM immutability can be broken then 
there's no need to mention wait/notify/etc. as there's a far more fundamental 
issue to hand. Immutability can *always* be broken given enough willfull 
vandalism.  It's probably best to just mark this down as risky behaviour - if 
you want to try it then you're on your own - in any sane normal usage it's 
completely reasonable to treat `final` as though it truly does mean 
"immutable", 
I try not to lose any sleep over it.

Incidentally, it's also reasonable for an immutable class to have a mutable 
subclass, this is also not a problem, but it does raise some questions about 
having an inheritable @immutable annotation...

As Reinier stated, there are some very deep issues when it comes to using 
@immutable as anything more than a hint.  Short of an effect-tracking system in 
the compiler, there's little more we can do.  Convention has to rule the day 
here.

So... Files!  The current Java implementation is so convoluted that there's 
nothing for it but to go back to first principles, sacrificing even object 
orientation if necessary to get to the truth of the matter (it can always be 
added back later).  Your chances of teasing any immutability out of the current 
class hierarchy are about as likely as winning the lottery.

However you look at it, things have to be changed around quite a bit to get 
this 
whole immutability business right. So let me take you on a journey, if I may...

---------------------

What is a file?

It's more than just a path, you can have a well formed path that refers to a 
non-existant file.

It's not just a sequence of bytes on a storage medium, it also involves other 
metadata - such as a path, modification timestamp, etc.

Then we have "files" under procfs on linux systems, and named pipes, and 
network 
streams - all accessible fia "file handles".  There are different and deeply 
interwoven concepts that are all known as "file" in different contexts.  Some 
of 
these can be immutable.


First, there's the path.  This is completely immutable.  Paths can have many 
operations, you can get the parent path, find the path of the some 
subdirectory(as a new immutable path instance), etc. Path needn't have an 
operation to determine if it's well-formed, as this can be done at construction 
time.

In the face of named pipes et al, paths should really just be represented as 
uris.  Path can then be introduced as a subclass of Uri, with an additional 
method to return the "native" representation.

There are also functions that operate on paths, but don't posess referential 
integrity, such as determining if a file exists at some path. Because side 
effects are involved, it's not valid for such functions to be methods on paths.

The next layer is some sort of File reference (or handle, moniker, etc. I've 
seen them all used before). I favour FileRef because it's concise and clear in 
meaning.  A FileRef *might* be immutable, though often isn't, a good 
copy-on-write filesystem would also be able to provide immutable FileRefs.  To 
go from a Uri to a FileRef, there needs to be a factory method capable of 
returning different FileRef subclasses. One such subclass would be 
NonExistentFileRef (which is immutable), another such subclass would be 
DirectoryRef.  Given that we're using URIs in lieu of paths, a FileRef could 
also represent a web page, remote FTP file, named pipe, etc.  It's doubtful 
that 
"file" ref is even a good name here, but it's historic and has a lot of 
stucking 
power.

As FileRefs can be immutable, they also mustn't contain methods that can't be 
free from side-effects.  So `delete` won't be an available operation - it has 
to 
be a separate function.  All you can do with a FileRef is obtain the path and 
metadata, and open the contents.  In the case of a directory, the contents 
would 
be a collection of contained FileRefs, for a network connection the contents 
would be a read-once stream, for a "standard" file they could be a stream or 
random-access buffer.  The exact subclass of FileRef (maybe combined with 
traits 
or interfaces) then determines exactly what form of "contents" are available.

To ensure that COW FileRefs are immutable, writing/modifying would then have to 
be done via function that transforms one instance of file contents to another 
instance.  This function is then passed to a method on FileRef that applies the 
translation and returns the modified FileRef instance.

DCI isn't necessary here, as all the relevant differences can be captured in 
the 
subclass hierarchy of FileRef.  This also neatly sidesteps any considerations 
about equality on roles :)

As for methods like `delete` or `rename`... they'd need to go in a utility 
class 
of static methods or a singleton. I'd call it `FileSystem`

On 13 March 2011 21:09, Alexey Zinger <[email protected]> wrote:

File systems -- and I/O in general -- tend to throw a big monkey wrench into 
the 
whole immutability conversation.  In some ways, this is where theory and 
reality 
of computing collide and some abstractions start to leak.  I suggest sometimes 
it's rather pointless to choice immutability to the bitter end if there is no 
real tangible benefit.  While we can agree that there are many design flaws in 
java.io in general, I think there's only so much designing that can be done 
around the fact that we need to be able to pass around references to entities 
that describe some persistence or I/O artifacts, and at the same time, by their 
nature, they exist with the purpose of having side-effects.
>
>In situations like this, I usually don't agonize over File and streams and so 
>on, but design  some architecture around the bigger concepts of persistence 
>itself and allow my framework to expose its state in thread-safe manner as 
>needed.  This lets me pass around some abstractions that have very well 
>defined 
>side-effects in a way that makes them "functional-friendly", while under the 
>hood my persistence framework might look a lot more procedural with fairly 
>traditional flow of control and synchronization patterns.
>
> Alexey
>2001 Honda CBR600F4i (CCS)
>1998 Honda RS125 (CCS)
>2002 Suzuki Bandit 1200S
>http://azinger.blogspot.com
>http://bsheet.sourceforge.net
>http://wcollage.sourceforge.net
>
>
>
>
>
>
________________________________
From: Fabrizio Giudici <[email protected]>
>To: The Java Posse <[email protected]>
>Sent: Sun, March 13, 2011 11:44:28 AM
>Subject: [The Java Posse] Some design questions (about immutability and other 
>stuff)
>
>
>A parallel discussion at the Lombok mailing list revamped some thoughts and 
>doubts about immutability. I'd like have some feedback about it.
>
>Given that immutability is a "turtles all the way down" thing ("turtleness" in 
>the following), a poster raised doubts about things such as File. File is 
>immutable for what concerns its internal state (the path), but all methods 
>that 
>actually work on the related file don't always return the same values (or do 
>the 
>same thing) because they refer to the actual file which is mutable.
>
>Such a class is regarded as mutable or immutable? Of course, I'm interested in 
>the practical purposes.
>
>Curiously, the "anemic object" antipattern is something that would help here. 
>If 
>you defined File only as a wrapper on a path, and FileSystem another class 
>which 
>accepts a File as argument, you would have clearly separated the mutable and 
>immutable portions.
>
>But I don't like anemic objects. When I can, I just  separate the two parts in 
>two different objects, but they stay bound together. Referring to some actual 
>code that I'm not using in production but for some tutorial, I have a:
>
>FileModel fileModel = new FileModel("/foo/bar");
>String path = fileModel.getPath();
>String name = fileModel.getName();
>
>and the above two methods are the only two specific methods of FileModel. So 
>far, it's anemic and immutable. Going on:
>
>fileModel.as(Removable).remove();
>long size = fileModel.as(SizedInBytes).getSize();
>List<FileModel> children = fileModel.as(Composite).findChildren().results();
>
>
>That as() is my way to deal with DCI and Semantic models. In simple words, it 
>just recovers a "role" of FileModel by referring to its interface - 
>as(Removable) is a shortcut for as(Removable.class). All the roles in the 
>example above are simple, stateless objects that work on the real  filesystem. 
>Clearly, I've separated behaviours: FileModel on its own encapsulates the 
>really 
>immutable portion, while roles keep the remainder behaviours. But there's no 
>turtleness in FileModel, because there's only a single level of turtles (i.e., 
>fileModel.as() always returns the same thing for each role, but the returned 
>object is not immutable in the strict definition).
>
>There are variations on the theme. Some roles are de facto hardwired in 
>FileModel; others can be dinamically injected by the context. But once a role 
>has been injected into a FileModel instance, it stays forever.
>
>How do you comment about immutableness of FileModel? As said, there's no 
>turtleness. It's enough to say that FileModel is not immutable?
>
>
>PS Another related question is about equals() and hashCode()... Are two 
>FileModel instances with the same path, but possibly different roles, 
>equals()? 
>I'd say not. But this is another question, and more related of  the broken 
>definition of equality in Java.
>
>-- Fabrizio Giudici - Java Architect, Project Manager
>Tidalwave s.a.s. - "We make Java work. Everywhere."
>java.net/blog/fabriziogiudici - www.tidalwave.it/people
>[email protected]
>
>-- 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.
>
>
>
>
-- 
>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.
>


-- 
Kevin Wright

gtalk / msn : [email protected]
mail: [email protected]

vibe / skype: kev.lee.wright
quora: http://www.quora.com/Kevin-Wright
twitter: @thecoda


"My point today is that, if we wish to count lines of code, we should not 
regard 
them as "lines produced" but as "lines spent": the current conventional wisdom 
is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra



      

-- 
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.

Reply via email to