8.1.2 Functors as abstract datatypes

Functors are abstract datatypes that are concretely realized using chunks. In order to be able to safely distinguish functors from other chunks, a special feature is added to the representation: a chunk is a functor iff it has this feature. The feature, displayed here as <N:functorID> is an Oz name. This is a common technique for implementing safe abstract datatypes, i. e. datatypes which cannot be forged/faked.

For example, we have seen before an implementation of a bag:

fun {NewBag}
   C={NewCell nil}
in 
   bag(put    : proc {$ X} L in {Exchange C L X|L} end 
       toList : fun  {$} {Access C} end)
end

We might test whether a given value is a bag by looking at the label of the record, but that's a test which is easy to fool. Instead, we can create a private name, known only to the implementation of the abstract datatype:

local 
   CELL_ID = {NewName}
in 
   fun {NewBag}
      C={NewCell nil}
   in 
      {NewChunk
       bag(
          CELL_ID : unit 
          push    : proc {$ X} L in {Exchange C L X|L} end 
          toList  : fun  {$} {Access C} end)}
   end 
   fun {IsBag X}
      {IsChunk X} andthen {HasFeature X CELL_ID}
   end 
end

NewChunk is a library function which takes a record as an argument and returns a chunk with the same features. Why doesn't NewBag return the record directly? Here is why:

declare R=foo(a:1) {Inspect {Arity R}}

By invoking Arity, anyone could obtain all the features of the record, including CELL_ID which was supposed to be private. By design, it is not possible to obtain the arity of a chunk, which makes chunk ideal for implementing type-safe abstract datatypes.


Denys Duchier, Claire Gardent and Joachim Niehren
Version 1.3.99 (20050412)