Plan 9's "everything is a file" philosophy never worked for me, because it's far too low level, and I don't believe Unix's file I/O API itself is very pleasant: open, read, write, ioctl and select (gag).

I'd much rather have a real API with rich app-specific functional or event interfaces to call, and to be able to pass actual typed parameters instead just a stream of bytes, including structured data like json, s-expressions or PostScript data, or even (gasp) Turing complete programs, like PostScript code!

NeFS, as defined in 1990 in the infamous NFS3 proposal aka "Network extensible File System Protocol Specification" did just that, and it was actually a great idea too early for its time. So it went over like a lead balloon, and was never actually adopted.

NeFS should not have been framed as a successor to NFS, because it required a revolution in how programs interacted with the file system. And of course there are the security and stability implications of running downloaded code in the kernel, which hadn't been properly addressed. ;)

Take for example (shown below) the act of copying a file to a backup file in the same directory.

With traditional NFS, the server would have to send each block of the file to the client, which would then send it back to the server, which would then write it to disk. That required a lot of network traffic, as well as many context switches between user and kernel space on both the client and the server (at a time in history where they were extremely expensive).

Instead, the client could just send a simple PostScript program to the server (or call one that was loaded from a library or sent previously -- see the example below), which copied the file in the kernel of the server, without sending it over the network, or requiring any context switches on either the client or server.

That's several orders of magnitude more efficient in terms of both CPU and network usage, and just the simplest and easiest to explain example possible of what you could do.

Just imagine how much more efficient, powerful and tightly integrated together other utilities like "find" and "grep" could be, tightly woven together procedurally in the kernel instead of communicating with a stream of bytes over a pipe!

http://www.donhopkins.com/home/nfs3_0.pdf

Introduction

The Network Extensible File System protocol(NeFS) provides transparent remote access to shared
file systems over networks. The NeFS protocol is designed to be machine, operating system,
network architecture, and transport protocol independent. This document is the draft specification
for the protocol. It will remain in draft form during a period of public review. Italicized comments
in the document are intended to present the rationale behind elements of the design and to raise
questions where there are doubts. Comments and suggestions on this draft specification are most
welcome.

1.1 The Network File System

The Network File System (NFS™*) has become a de facto standard distributed file system. Since
it was first made generally available in 1985 it has been licensed by more than 120 companies. If
the NFS protocol has been so successful why does there need to be NeFS ? Because the NFS protocol
has deficiencies and limitations that become more apparent and troublesome as it grows older.

1. Size limitations. The NFS version 2 protocol limits filehandles to 32 bytes, file sizes to the
magnitude of a signed 32 bit integer, timestamp accuracy to 1 second. These and other limits
need to be extended to cope with current and future demands.

2. Non-idempotent procedures. A significant number of the NFS procedures are not idempotent.
In certain circumstances these procedures can fail unexpectedly if retried by the client. It is not
always clear how the client should recover from such a failure.

3. Unix®† bias. The NFS protocol was designed and first implemented in a Unix environment.
This bias is reflected in the protocol: there is no support for record-oriented files, file versions
or non-Unix file attributes. This bias must be removed if NFS is to be truly machine and
operating system independent.

4. No access procedure. Numerous security problems and program anomalies are attributable to
the fact that clients have no facility to ask a server whether they have permission to carry out
certain operations.

5. No facility to support atomic filesystem operations. For instance the POSIX O_EXCL flag
makes a requirement for exclusive file creation. This cannot be guaranteed to work via the NFS
protocol without the support of an auxiliary locking service. Similarly there is no way for a
client to guarantee that data written to a file is appended to the current end of the file.

6. Performance. The NFS version 2 protocol provides a fixed set of operations between client and
server. While a degree of client caching can significantly reduce the amount of client-server
interaction, a level of interaction is required just to maintain cache consistency and there yet
remain many examples of high client-server interaction that cannot be reduced by caching. The
problem becomes more acute when a client’s set of filesystem operations does not map cleanly
into the set of NFS procedures.

1.2 The Network Extensible File System

NeFS addresses the problems just described. Although a draft specification for a revised version of
the NFS protocol has addressed many of the deficiencies of NFS version 2, it has not made non-Unix
implementations easier, not does it provide opportunities for performance improvements. Indeed,
the extra complexity introduced by modifications to the NFS protocol makes all implementations
more difficult. A revised NFS protocol does not appear to be an attractive alternative to the existing
protocol.

Although it has features in common with NFS, NeFS is a radical departure from NFS. The NFS
protocol is built according to a Remote Procedure Call model (RPC) where filesystem operations
are mapped across the network as remote procedure calls. The NeFS protocol abandons this model
in favor of an interpretive model in which the filesystem operations become operators in an
interpreted language. Clients send their requests to the server as programs to be interpreted.
Execution of the request by the server’s interpreter results in the filesystem operations being invoked
and results returned to the client. Using the interpretive model, filesystem operations can be defined
more simply. Clients can build arbitrarily complex requests from these simple operations.

[...]

Example: Copy a File

Make a copy of file (foo) called (bar). Both files exist in the same directory dfh. The request
starts by looking up the filehandle for the file to be copied and creates a filehandle for the copy.
The loop operator executes a procedure that copies the file using 1K reads and writes. It
maintains a running count of the number of bytes yet to be copied.

% Copy a file
%
dfh (foo) lookup /foofh exch def % get filehandle for (foo)
dfh (bar) create /barfh exch def % create filehandle for (bar)
/bytes foofh getattr /fsize get def % get size of (foo) so we know how much to copy
/offset 0 def % initialize offset for (bar)
{
/data foofh offset 1024 read def % read up to 1K from (foo)
barfh offset data write % write up to 1K to (bar)
/bytes bytes 1024 sub def % decrement byte count by 1024
bytes 0 le { exit } if % if it’s < 0 then we’re done
/offset offset 1024 add def % increment offset by 1024
}
loop
barfh getattr 1 encodereply sendreply % return the attributes of the new file to client.