I see a small problem with the Scala stuff.  Specifically:


  FileRef
  - DirectoryFileRef
  - StreamableFileRef
    - AppendableFileRef
      - RandomAccessFileRef
  - NonExistantFileRef

How does this address the scenario of a non-existent file reference "becoming" 
existent by a side-effect of code elsewhere (maybe outside current execution 
thread, or the JVM as a whole)?  But in general, this just feels like a losing 
battle to me -- trying to apply immutability principles to I/O.  Why?
 Alexey





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




On 14 March 2011 10:40, Fabrizio Giudici <[email protected]> wrote:

On 03/14/2011 11:04 AM, Kevin Wright wrote:
>
>What a tangled web we weave...
>>
>>

OK, Kevin. Bytecode manipulation apart (you correctly depicted the scenario as 
"vandalism" in this case and can't be avoided), does Scala add something 
interesting to the topic? :-) I'm not pulling your legs, I'm really interested 
to know whether other languages deal with it in a better way, or if in the end 
it's only a matter of design.
>
>


There's a few bits that Scala helps with.  The deep support for immutability 
makes it far easier to get this code right (you don't have to worry about 
missing a `final`), the collection library is a much better fit, type inference 
and implicit conversions also makes some design patterns a lot more practical, 
and pattern matching is a natural fit for working with immutable objects.  The 
syntactic-sugar/eye candy also helps make the intent of the code far clearer. 
 First class functions/closures are very relevant too  

Getting really deep, there's also a lot that can be done with type classes and 
monadic comprehension to make the code cleaner and more generic, but it's far 
from essential and I don't want to scare anyone off :)

File handling is such a rich topic that it's hard to know what will interest 
people, so I'm just going to throw out a brain dump with some random stuff...

m'kay?


As always, it's easier to show with some examples:

  val somedir = FileRef of "C:\somedirectory"

In this case, I'm invoking the `of` factory method on the `FileRef` singleton. 
 This method accepts a URI argument, so you're also using an implicit 
conversion 
from String=>Uri.  De-sugaring, it's equivalent to:

  val dir: FileRef = FileRef.of(Uri("C:\somedirectory"))


So how about those refs? Assuming here that we have a hierarchy something like:

  FileRef
  - DirectoryFileRef
  - StreamableFileRef
    - AppendableFileRef
      - RandomAccessFileRef
  - NonExistantFileRef

That FileRef isn't much use as it stands, the concrete instance could be any of 
those subclasses, so to print a directory list you might use pattern matching:

  dir match {
    case x: DirectoryFileRef => println(x.contents mkString "\n")
    case _ => error(dir.toString + " is not a directory)
  }

Building on the API a bit more, you could also have an asDirectory method on 
FileRef, which would return an `Option[DirectoryRef]`. Allowing that example to 
be rewritten:

  dir.asDirectory foreach { d => println(d.contents mkString "\n") }

(think of Option as a collection with 0 or 1 elements, where we're supplying a 
closure to be used for each element in that collection)


Combined with `containedFiles` and `subdirectories` as methods on 
DirectoryFileRef, you can write code like this to print the name of the first 
file in the first subdirectory:


  val dir = (FileRef of "C:\somedirectory").asDirectory
  val subDirs = dir flatMap { _.subdirectories }
  val firstSubDir = subDirs.headOption
  val subdirEntries = firstSubDir flatMap {_.containedFiles}
  val firstSubEntry = subdirEntries.headOption
  firstSubEntry foreach { println("first subentry is " + _) }

`headOption` will return the first element of a collection as an Option (None 
if 
it's called on an empty collection).  So no risk of NPEs here!

It's guaranteed not to throw an exception (notwithstanding some catastrophic 
problem in the filesystem), but still a bit verbose.

You can also do the same thing more cleanly with a for-comprehension.  
Including 
filtering and returning a collection for further processing:

  val tmpFiles = for {
    dir <- fileRef.asDirectory
    subdir <- dir.subdirectories filter (_.name startsWith "temp")
    files <- subdir.containedFiles filter (_.extension == "tmp")
    file <- files
  } yield file

Much cleaner :)


To work with file contents, you can then use closures, following a pattern 
similar to Jdbc Templates in Spring.
Given an update method that accepts a RandomAccessFileRef and a closure, you 
can 
then write (glossing over the need to handle string and byte-based files 
differently):

  val fileRef: RandomAccessFileRef = ...
  File.update(fileRef) { contents => contents ++ "New last line\n" }

or

  File.update(fileRef){ someLongComplicatedFunctionTakingAContentsArgument }

This would handle resource management, file opening/closing, etc. for you.

Could probably think of a lot more, but I've already rambled far too much as it 
is.




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