Let’s imagine we live in a late binding world, a programming world of messages, what should that look like? What does it look like in the large?
The Smalltalk notion of this is many, many independent referencable objects. Objects all the way down. So you might get something like buddies do: [ buddy | buddy sendMessage: myMessage].
At runtime this means something like:
- Send the buddies object (some reference we obtained elsewhere) the do: message, with the argument [ buddy | buddy sendMessage: myMessage]. Except remember that that [...] is a block (aka closure), and it’s also an object. Which only makes sense, because buddies doesn’t know what myMessage means. So all buddies knows is that it gets an argument.
- The buddies object receives this do: message, with an argument we’ll call block (that [...] stuff). By convention do: is sent to sequence-like things, and calls its argument multiple times. So buddies figures out some items — maybe it’s a concrete sequence (like an Array) or maybe it looks it up in a database, or does a call over the net, or whatever.
- Once buddies gets an item, it calls block value: item. That is, it sends a value: message back to the block object, with the argument item.
- The block has just received that value: message. The block wouldn’t have to be that [...] syntactic construct, it just usually is. So it receives that value, and locally binds the buddy variable to the argument and evaluates the inside of that block.
- Our expression then sends the sendMessage: message to that buddy object, with another object as the argument. That argument might be a string, but it also might be markup, or a richer object that itself does all sorts of magical things. Maybe the buddy object sends the object over the net to your remote buddy, then the remote buddy calls message displayOn: myScreen, and the message object reponds to displayOn: by sending screen showText: someString withColor: #blue. This can go on for a while.
As you can see, there can be a lot of tracing through code in this style. This has been called Ravioli code. Many people lean heavily on concepts like classes and methods to make sense of this kind of code, though if you look through my description you’ll notice they don’t show up; it is apparently somewhat frustrating for Alan Kay that he first used the term "object-oriented" when he would have preferred this message-oriented concept. Classes and methods are just implementation details. Messages and object references are the true fundamental concept.
Doing this in the large means that a lot of these messages go over the network. It means we can get a handle to an object that lives on another computer. It might result in a system that is very chatty (many systems with transparent RPC do end up being unintentionally chatty). But it doesn’t have to. You can make this asynchronous to facilitate these kinds of efficiencies.
That said, there is another distinct technique for programming in the large: document oriented programming. This is what REST is focused on: transmitting state in as complete a form as feasible, with explicit references whenever referring to another resource.
Resources and objects are in many ways similar, but in other ways completely opposite. An object has behavior but no visible state. It responds to messages, passing more objects along. Resources themselves have no behavior. They are just bare state. REST and the web kind of adds behavior, but strict REST avoids even that. PUT is not behavior. PUT is almost like a declaration of fact. When you PUT to a resource you are claiming that the new state of the resource represents the truth of that resource. Any behavior that occurs because of that is hidden. Other resources may change as a result of the PUT, but this is not exposed in any way. To find out what’s changed you simply have to refresh all the state you’ve acquired — re-GET the resources. (POST, except when used simply to create a resource, is more like a verb than a statement of truth; the HTTP model is not pure in this respect.)
For programming on a web scale resources have become the dominant technique. REST was really an acknowledgement of this idea, not the invention of it. It’s also an advocacy effort that our future work should be built on this past success. It’s been focused very clearly on advocating resources over the object/message-based systems of WS-*, but the WS-* design is hardly the first or only object based system of its kind; anything called "RPC" comes from that line of thinking. But now that WS-* isn’t king of the hill anymore maybe we can more be more thoughtful about how REST relates to other programming techniques.
A way to describe the difference in terms of programming language design is that with resources everything we do is call by value. Resources are values, and we pass them around in their complete form. You can pass around a reference value (a URI), but this is like calling a function with a pointer argument. Object/RPC techniques are call by reference (though at some level there are true "values", things like integers and strings, as things would simply be too chatty otherwise).
One interesting place where objects and resources can overlap is in functional programming. Thinking in terms of call by value or call by reference, in a functional programming language these are identical. Because values are not mutable a value and a copy of the value are equivalent. You can also pass an object between systems by reference, in whole, or some lazy compromise where the object is only incrementally communicated between the processes.
Resources are in a sense immutable regardless of your programming language. When you retrieve a resource you cannot modify it in place, as the result is no longer representative of what the resource really is. You can put a new resource in a named location (PUT), and you can copy resources (maybe making an internal copy). Incidentally this is why I think literal document models are best. It would probably be useful to have a native representation of what a "resource" really is in programming languages, though usually this is not done. A resource should really be a document (content type, some bytes, perhaps a cache of parsed representations) plus the name (URI) and the time. Resources aren’t eternal, but given a time and URI there is (in a REST world) a single canonical representation.
Further investigations of functional programming languages would probably be useful here. I believe strict functional languages have interesting metaphors for handling this kind of information; the eternal immutable sense of what a thing is at a point in time, and a way to reconcile the incomplete knowledge of what a resource is right now. I don’t think this is a clear pattern in Erlang, but is in Haskell.
One useful aspect of resources is the ability to spy on the process, to get a snapshot of it, to understand it in a kind of literal way. This is view source in a browser, or just poking around with curl. You can feel a certain confidence that you can get a real sense of what the truth is using these tools. In an object-based system you can only poke around with a process of 20 questions, hoping that there isn’t some hidden truth that you are unaware of. For instance, talking to a proxy that is almost exactly like the original object, but with some small replacement. Of course internally a server can be peculiarly coupled as well, with relationships between the resources that aren’t explicit or obvious. But at least the faces it projects are fully formed.
I still haven’t fully figured out what distinguishes a document model from an object model, but I think the distinction between the two contains some interesting stuff worth exploring.
No related posts.
Great post. Completely concur, but am reticent to use the term “documents” since a lot of people seem to think that implies XML or maybe HTML. Seems weird to talk about JSON “documents”.
I’ve been trying to push the term “data structures” for this. That’s not the best fit either, in general, but makes sense for JSON.
The way I think about references, values and immutability is is collaborative documenting. Take a team of people developping a document together in a company. All documents are either stored on a common filesystem or sent as copies via email. If all modifications to the document result in new documents with new version numbers, then the documents are immutable. If multiple people are working on the same document, it’s a good practice to make a copy for each modification, so the path of modifications become apparent and no modifications are surrepticiously erased when two people save almost simultaneously. By mail, of course, the copies sent are values. Eventually no one knows which version is the right one. I think the best way of working is to save all documents in the same place (ressource) and send mails with references to that place.
The hard part is, nevertheless, merging forked versions. This is adressed in version configuration systems because there are people assisting in the merge process, but there are no solutions that I know of for software archtitectures.
“”" A resource should really be a document (content type, some bytes, perhaps a cache of parsed representations) plus the name (URI) and the time. Resources aren’t eternal, but given a time and URI there is (in a REST world) a single canonical representation. “”"
My immediate though in response to this, is that svn (or any other version control system), addresses this issue exactly. You can get a resource, as it is currently (HEAD), or you can retrieve a particular revision (URI+time).
(Btw. you comment system doesn’t format as markdown, even though it says so)
Interesting thoughts there.
Consider MFC document-view architecture also, the lines are blurred somewhat between objects and documents as you describe: there is the undo/redo history (state changes) and the view provides a complete snapshot of state at any one time. However, the document is undeniably an MFC object. I suppose the paradigm is dependent on whether you’re a user or a developer, whereas in REST architectures it’s the same…
Interesting post.
Speaking of document-oriented systems, I think that monads could make for some very interesting applications.
In a document-oriented system (say an interactive web-app) written in a language like Haskell (where state is explicitly isolated), state (which would be contained in a set of monads) could be sent to the client with each response. The state could be something like a continuation.
In this sense, one would have a continuation-based system (like Seaside), but instead of the state residing on the server, it resides with the client. So all documents (in the web-app) can remain immutable.
Researchers at Cornell have developed a stateless network stack based on this idea. It’s called “Trickles” (http://www.cs.cornell.edu/~ashieh/trickles/protocol.php).
This system will probably be impractical if the client state requires a lot of storage space. Perhaps a way around this problem is a hybrid approach where some state is sent to the client and where new documents are created on the server (using something like PUT) to represent the rest of the client state.
But this is pretty much back to the Seaside model (although given my lack of knowledge of the inner workings of Seaside, I don’t know whether client state can be reified as a set of documents).
Dave:
The mapping between what you describe and Ian’s description of REST would seem to be “view -> uri, (view,state) -> (uri,tick)”.
Ian:
Really good stuff.
The pair (uri,tick) can also be represented as object/document, at a uri. Immutable and reference-able resource.
“You can pass around a reference value (a URI), but this is like calling a function with a pointer argument.”
URIs are unlike pointers in that they don’t refer to the same “memory address” (call-by-value, your point), modifications are not necessarily symmetric. For immutable resources, you can ignore the distinction, but signalling a reference’s immutability isn’t something I’ve seen REST try to handle. (Caching with etag/if-not-modified does allow you to grossly detect when immutability becomes non-operative.)
URIs are like pointers in that we can perform some key operations without dereferencing a given uri. Relational join, index/hash lookup; metadata. Microformats, RDF, LinkedData are all focused around expanding the pool of data you know about things you may only have heard of, not yet seen or touched. In immutable/by-value, this property is perhaps associated with lazy evaluation.
On the distinction between document and object models, inheritance is something to consider (or say, polymorphism, to stick with your intro’s avoidance of classes). Date and Darwen in Third Manifesto make a solid argument that an (object model with object-ids) cannot support an (inheritance model with generalization/specialization-by-constraint). It would be interesting to see if the differences between URIs and objectIDs/pointers remove that limitation.
“signalling a reference’s immutability isn’t something I’ve seen REST try to handle”
Probably more correct to say that HTTP doesn’t have a way of signalling immutability; REST doesn’t add anything like that to the equation anyway.
This has come up before, on some other blog thread, because there are some fairly clear use-cases for it. The most obvious one I’ve run into is pre-packaged versions of huge JS libraries (dojo, yui), which, at least in production, you could certainly treat as immutable by using a version number in the URL itself (presumably a directory name).
Your Smalltalk code is wrong, it should be:
note the colon on the first buddy, the period is actually optional
Starting from a more mundane origin, the web.py framework gives you controllers with methods corresponding to HTTP methods, which is a good starting point for designing a web app. around the notion of resources. But what when you want to compose a web page out of multiple resources; it would be nice to be able to call the corresponding web.py controllers via their externally facing methods.
Unfortunately you don’t know what the side effects will be – a controller you call might start sending undesirable HTTP headers such as content-length. Also it would be nice, if the call would be rather more than just a method-call; acting like an HTTP client, calling a server so, for example, caching headers can be employed. So perhaps it would be nice to unify fetching a remote resource with fetching one “internally” from memory.
Meanwhile, given a URI for a resource and it’s value (the document), HTTP provides mechanisms for comparing the value you currently have the source you got it from, from modification time to direct comparison via something checksum-like (E-Tags). In most programming languages, when you receive a copy of some variable, you first don’t really have a way to know it’s source and comparisons of values may be [difficult and expensive](http://cpan.uwinnipeg.ca/dist/Data-Compare).
Perhaps those are problems that would be interesting to solve at a language level?
Great post.
I liked the way you started with late binding.
The fact is, the resource model (eg. web) gives up on late-binding, gives up on polymorphism (there’s no ambiguity about what’s at the end of the URL), and doesn’t really encapsulate behaviour much at all.
That turns out to be OK.
But does it, in fact, turn out to be OK because the web as we understand it simply doesn’t have much behaviour? And so ReST is sufficient to keep it under-control?
Or does it turn out to be OK because, in fact, the extra complexity of managing the indirection needed to give you later-binding makes it impossible to scale? And so programming in the largest large means giving up on hopes of polymorphism altogether … and so living with the redundancy?
Like many have pointed out, if you think of document/resource based system as a version/difference system, then we can easily see the differences between the resource and the object model. In its purest form a REST/resource based model can be seen as an explicit state representation. You “do” something to the state by adding or moving things (documents) around (think of destruction of the state as a relocation of resource, maybe to /dev/null?). You have a full history of what is going on and things are always additive. Mixing in the time dimension, (current) state of the system is basically a snapshot of what you have added to the system so far. If you stop in time, and check out (punt intended) the “current” state of the system and how it’s functioning, there is no difference between object-based and resource-based systems. However, with a resource-based system, you have (or at least in theory) the whole history of what you’ve have done. You can look at states other than the “current” one to compare and contrast what your have done. Where as an object-based system tend to confine you to only the current state, and you have to infer what has happened. Maybe this is why you can “feel a certain confidence” towards a resource-based system, or you “can get a real sense of what the truth is”. But do keep in mind that too much information can overwhelm a system and its users. There is always a balance/trade-off in an imperfect world. Maybe we should start thinking about what all these mean in quantum computing… hm… the possibilities… :)
“An object has behavior but no visible state”
In REST you want to represent the state so that you can transfer it over the network (yeah, duh). In many forms of programming we often work almost exclusively with objects. Since the state of an object is encapsulated by behaviour it can feel like trying to thread a needle with oven mitts on, “dear object, tell me what you are so that I may better represent you!”
“In an object-based system you can only poke around with a process of 20 questions, hoping that there isn’t some hidden truth that you are unaware of. For instance, talking to a proxy that is almost exactly like the original object, but with some small replacement. Of course internally a server can be peculiarly coupled as well, with relationships between the resources that aren’t explicit or obvious. But at least the faces it projects are fully formed.”
Ha! Twenty questions is a good analogy. Objects can act according to Heisenberg’s uncertainty principle, whereby the act of observing an object changes the object itself. This can be mitigated if you do not treat all objects as conceptually equal. If you have a category of objects whose only purpose is to describe the outward face of another object – including a description of state of the object it describes, then you don’t need to play 20 questions with an object to get a sense of an objects outward face. COM and zope.interface/zope.schema provide such things, but then these are only loose promises of what they think the state of an object is. If your object is a proxy object or has other comlexities then when it comes time to extract state it may not be what you expect.
In these ways of thinking we say interface and implementation. A description of behaviour (interface) and the many possibilities of that behaviour (implementation). There are interesting parallels between this and resources and documents. A state (document) and the many possibilities of that state over time (resource).
Infinite behviour mutliplied by infinite state and the mind is pleasantly boggled … :)