Re: Maybe we should think about Interface.isInterface functions again

On Wed, Jul 31, 2013 at 11:50 AM, Allen Wirfs-Brock
<allen@wirfs-brock.com> wrote:
>
> On Jul 31, 2013, at 12:02 AM, Jonas Sicking wrote:
>> It's not entirely clear to me how you mean that the isDirectory
>> function would behave, or where it would live. Do you mean that we
>> would add a isDirectory() function on all Directory instances which
>> always return true?
>>
>> That would mean that you write code like:
>>
>> root.get("somename").then(function(result) {
>>  if ("isDirectory" in result) {
>>    // No need to actually call .isDirectory since it always returns true.
>>    ... it's a directory ...
>>  }
>>  else {
>>    ... it's a file ...
>>  }
>> });
>>
>> This approach definitely works. Basically any time that you can have
>> objects of different types and want to find out what type of object
>> you have, you have to find some property that is unique for each type
>> that you want to check for.
>>
>> The downside of this approach is that it's pretty fragile though. Any
>> time a property is added to existing classes, it risks creating a
>> situation where you are breaking type testing.
>>
>>
> something like that, but I would do it something like:
>
> //first approach:
>
> I assume that you have File and Directory objects and that, in some sense,
> Directories are also Files.  In other words, Directory might be implemented
> as a subclass of File.
>
> I would define the property "isDirectory" on both File.prototype and
> Directory.prototype For files its value is false and for Directories its
> value is true.
>
> I would test it as:
>   if (result.isDirectory) {... //do directory specific processing
>   ...
>
> Note that if result was neither a Directory or File and does not have such a
> property, result.isDirectory is a falsy value so the directory path of the
> logic will not be taken.  Strictly speaking,  File.prototype does not need
> to have a 'isDirectory' property but it is probably a better design to be
> explicit about such things.
>
> This is a reasonable approach, but lets consider how it might be used and
> how we might want to extend it in the future.

I don't really think this approach scales. Another example of where
this occurs is when handling binary data. Right now browsers provide
two ways of doing so: ArrayBuffers and Blobs. Blobs are more
appropriate when handling large pieces of data since the data can be
stored on disk, ArrayBuffer is better when handling small pieces of
data since since the synchronous reading makes it faster and easier to
use.

So I could see code that wants to support both types of data. For
example the WebSocket interface supports both sending ArrayBuffers or
Blobs. So I could imagine other libraries wanting to implement APIs
that do the same.

Does this mean that we should add ArrayBuffer.isBlob?

> //second approach
> Presumably, the need to discriminate between a File and a Directory is most
> likely to arise when trying to traverse directory structures.
>
> I assume that Directory probably has a method (let's call it 'itemsDo' for
> iterating over the files and subdirectories contained in a Directory.  Then
> we might write a traversal such as this using the above API:
>
> rootDir.itemsDo(function visit(fileOrDir)  {
>    console.log(fileOrDir.name);
>    If (fileOrDir.isDirectory) fileOrDir.itemsDo(visit)
> });
>
> Now what happens, if we want to extend the file system to recognize zip
> files and for traversal purposes we want to treat them as sub-item
> containers similar to Directories. Should we make 'isDirectory' true for zip
> files? We might to make the above traversal work.  But what if there are
> other directory behaviors that zip files don't support?
>
> A different API design might make this extension easier to do.  Note that
> this traversal really doesn't need to know whether an item is a File or
> Directory.  It only needs to know if an item has subitems that are
> retrievable using itemsDo. If itemsDo was defined for both File and
> Directory we could have coded the traversal as:
>
> rootDir.itemsDo(function visitot(dirItem)  {
>    console.log(dirItem.name);
>    dirItem.itemsDo(visitor)
> });
>
> assuming that:
> File.prototype.itemsDo = function (visitor) {return};  //files don't have
> subitems.
>
> Note that this enables the traversal visitor to be written without an
> explicit test WRT whether an item is a Directory or not.
>
> It will work even if we don't include 'isDirectory' in our API design.
>
> Using this design, if we want to add zip files we don't have to classify
> them as a kind of Directory.  All we need to do is define
> ZipFile.prototype.itemsDo such that it returns appropriate items for a zip
> files contents.
>
> This polymorphically dispatched  behavioral  style of API design has proven
> very successful for dynamic object oriented languages.  Rather than trying
> to identify specific implementation "classes" for various objects, this
> approach focuses is on identifying the common behaviors that need to be
> support by objects from different classes and implementing those behaviors
> in a manner that makes class identification irrelevant.

This approach would partially solve iteration. It would let you
iterate over all files and directories in the filesystem. But if you
wanted to actually do something with, other than simply checking their
names, you likely would want to know which ones are files and which
ones aren't. So if you wanted to find all gif files in the filesystem,
by checking for the gif file signature in the beginning of the file
contents, you would still want to check which entries are files and
which are directories.

With the current FileReader API you would have to try to read them all
and handle the error that is produced when trying to read from a
Directory.

I have made a proposal [1] for an improved API for reading from files.
Using that API it would be possible to check for the existence of
dirItem.readBinary and if that's there call that function with the
assumption that it is a file rather than a directory. So I agree that
with very careful design of APIs then we could solve this use case.
I'm far from convinced that we'll be able to design all APIs that will
though. Nor that all existing APIs are designed as well.

It also requires expanding the definition of a File object. Currently
a File object is used in many places outside of filesystems and is
designed to be independent of it. So in all cases where File objects
are used outside of filesystems the .itemsDo function makes not sense.

Also, what does this mean that we should do for a Directory.get(name)
function. Currently it is drafted to (asynchronously) return a File
object or a Directory object. Should we replace that with separate
Directory.getDirectory(name) and Directory.getFile(name) which produce
an error if you use the "wrong" one.

So I guess my general meta-point here is that I think type detection
is needed. The fact that "instanceof" does see a fair amount of usage
seems to indicate that authors feel the need for it.

[1] http://lists.w3.org/Archives/Public/public-webapps/2013AprJun/0727.html

/ Jonas

Received on Friday, 2 August 2013 06:19:57 UTC