RFC 242

Data Descriptive Language for Shared Data

πŸ“… published July 1971, from ietf

RFC 242 (NIC 7672) πŸŽ€

DATA DESCRIPTIVE LANGUAGE FOR SHARED DATA πŸ’–

Okay bestie, this one's from July 19, 1971 πŸ“ and it's by L. Haibt and A. Mullery from the Thomas J. Watson Research Center in Yorktown Heights, N.Y. 🧚 It's filed under Categories: D.4, D.7. The title block doesn't list anything it obsoletes or updates, so it's all fresh. ✨


Introduction 🌸

A big consequence of using networks of computers is that everyone suddenly wants to share data way more efficiently 😭

Lowkey, a lot of the problems with sharing data come from the many different ways people represent and refer to the same data. Almost all of these problems were already around before data got shared over computer networks. The network just made them way more obvious πŸ™ˆ

To talk about this clearly, the representation differences get sorted into three categories. πŸ’…

The first is very local representation: the bit patterns for the character set, and for fixed point and floating point numbers. These differences usually come from differences in CPUs and storage devices. Translating at this level can usually be done one unit at a time (for example, computer word by computer word). The biggest headaches happen when some values in one representation scheme have no matching meaning in the other, like trying to translate eight-bit bytes into six-bit bytes. πŸ’€

The second category is about how collections of data are represented, like their size, ordering, and location. πŸ¦‹

The third category is a bit harder to pin down. It's about the more complex structures that data collections can have. Think files with indexes, fields with internal pointers and cross references, and collections of files like partitioned data sets and generation data sets in OS 360. It's giving complicated πŸ‘‘

The approach the Network/440 project took was to develop a descriptive language. It lets you specify the parts of data representation that are going to get transformed when data moves around a network. Then the network data management system can look at those descriptions whenever it needs them for its data management work. For example, you could hand the data manager two descriptions: one showing how the data is represented right now, and one showing how a copy should look. The data management system can then run the transformations needed to make the right copy. Iconic, honestly πŸ”₯

This way of specifying data transformation is different from systems like the RAND Form Machine. Those give you a formalism for writing the specific translation algorithms that change one form into another. The descriptor-to-descriptor approach seems to make creating new field formats way less of a programming burden ✨ And neither method rules out using a Network Standard Representation 🫢


Structure πŸŽ€

The descriptive language assumes that data can have an inherent structure that's independent of other groupings, like name groupings, locking groupings, and so on, that get put on top of it. A data structure description is made of groupings of established data value type codes. The list of established data value types should be enough, through appropriate groupings, to describe any hierarchical structure of data. 🧚

The data type tells you how a data value is supposed to be interpreted. Here's the list of data type codes. This list must be able to identify each data type that may exist in a data set on any machine in the network. However, for data sets that only contain data types from the machine where they're stored, you don't need a different code for each form of a single type across different machines. The data type in the description, plus the identity of the machine where the data is stored, is enough to completely describe all of those forms. A tentative list of machine dependent type codes, compiled by G. Howe and T. Kibler, looks like this: πŸ’–

F    floating point

I    fixed

D    double precision floating point

C    character string

X    complex

P    packed decimal

L    logical

It's desirable to be able to build data sets that contain data types that aren't allowed at the machine where the data set is stored. Or even types that don't exist at any machine in the network. For example, you might want to store eight bit data on a six bit machine. In principle, at least, this can be done by defining a logical data set made of eight bit bytes, in terms of a real data set made of six bit characters. For this to work, though, the data value type descriptors have to be machine independent. The basic machine independent data type is:

B    bit.

It isn't clear yet whether any others are needed, since others can be built from this one. For convenience, other standard machine independent data types may be defined later. πŸ¦‹

Two other machine independent types are useful for describing structures:

Z    null

O    omit.

The null type means there's no data corresponding to this item. But the item should still be counted as existing in the structure. The omit type is the opposite: there is data that should not be counted as an item, and it should be ignored. πŸ™ˆ

A grouping of data values is described by listing its elements inside parentheses. Each element of a grouping can be either a data value described by one of the data value type codes, or another grouping. The elements are separated by commas, and they appear in the grouping in the order they're listed. For example, this description:

((C,C),(F,F,I))

describes a data collection made of two subgroupings. The first subgrouping has two data values of type C. The second subgrouping has two data values of type F, followed by one data value of type I. So the structure of this data collection is a three level tree, which can be drawn in two dimensions like this: 🌈

( )
 |
( )---( )
 |     |
 C-C   F-F-I

Properties πŸ’…

Other properties of the data, beyond its structure and composition, also need to be described. These can be assigned to items of the data collection. An item can be an individual data value or a grouping of them. You attach a property to an item by modifying its item description with the specification of the properties that apply to it. The notation is an infix notation of this form:

operand operator |[extent]|

The operator says what type of property it is. The operand is the property value. The optional extent is the number of items the property applies to. Normally the property just applies to the item right after it in the description. If the property should apply to more than just the following item description, you specify a number as the extent. That number tells you how many of the following item definitions at the same level the property applies to. ✨

Type - The structure description of a data set is a constitutional or syntactic description of it. Sometimes you also need to describe the use or meaning of an element. For example, in some complex data structures, the linkages between parts of the structure may be represented as data values inside the data set. So even though the more complex structure is written in hierarchical form and can be described with the notation above, the data values that represent the linkages, and what they mean, still have to be represented in the data description. Otherwise the complex structure isn't truly described. As another example, you might want to give some level of the data structure the type record, so that the data set can be used by a system that uses the concept of a "record" to access the data. 🧚

What an initial set of such types should be hasn't been decided yet. 🌸

Names - Items of a data structure can be given names by modifying the item description with a notation of this form:

name n |[extent]|.

Depending on the context of its use, the name can refer to the description itself, or to the data for the named part of the description. A name is assumed to be unique only within the scope of the extent of the next higher encompassing name. The exception is when you say otherwise by giving another encompassing name as the scope. The scope could be the name of the whole data set or description, for example. The scope of a name is specified by putting the outer name (or names) in front of the inner name, separated by dots. The name:

A,BETA

indicates that the scope of the name BETA is A. πŸ¦‹

The name applies to just the following item in the description, unless you say otherwise by including the extent parameter. For example, this description:

(An(C,C), (Bn[ 2 ]F,Cn[ 2 ]F,I))

means that name A is given to the item that contains two data values of type C. Name B goes to two data values, both of type F. Name C goes to the last two data values, one of type F and the other of type I. Notice that with this notation, extents can overlap. In the example above, the extent of name B overlaps with the extent of name C. 🌈

In a description, the same name can be applied to more than one item definition. You can do this either with the extent parameter, or by specifying the name at each item you want in the extent of the name. If a name is applied more than once within the same scope, the name is assumed to apply to the aggregate of the items it was given to. This means you can apply names to aggregates of items that aren't necessarily sequential. πŸ’–

Lock - While data is being processed, it may be necessary to lock out other users from using some portion of it. You can split a data set into units for locking purposes using this notation:

k|[extent]|.

Whether or not the data is locked, and what type of lock is applied (for example, write protect, or read/write protect), gets specified at the time the data is used. πŸ”’βœ¨

Authorization - Whether a user can access data may be controlled by an access code assigned to the data. You can put this into the description by modifying the desired elements of the description with an indication of the code. The notation is:

code a |[extent ]|

Control 🧚

There are two modifiers that control whether items in the definition exist. The first is the repetition modifier:

factor r |[extent ]|.

This causes the following item definition (or definitions, if the extent indicates more than one) to be repeated. So this description:

(3rC)

is equivalent to this one:

(C,C,C).

The other control modifier is the condition modifier:

condition c |[extent ]|.

If the specified condition is not true, the following item definition is ignored. The condition is specified by a Boolean expression. πŸ’…

Since several modifiers can apply to one item definition, there's a question about how they relate to each other. For example, if a repetition modifier and a conditional modifier both apply to an item, does the condition apply to all the repeated items, or only to the first one, assuming the extent of the condition modifier is one? πŸ€” The effect of multiple modifiers depends on the order in which they're evaluated. Two possible conventions come to mind. One says that

so, repetitions get expanded first, then properties are applied, and finally conditions are applied to the resulting expanded item definitions, no matter what order the modifiers were written in the description. πŸ’… Here's a quick example:

(A=3c [ 4 ]4rF,I)

is equivalent to

(4rA=3c[ 4 ]F,T)

and if the condition is true, it's equivalent to

(F,F,F,F,I)

or, if the condition is not true, it's equivalent to

(T)

πŸŽ€ The other convention is that the modifiers get evaluated in the order they show up in the description, or maybe in reverse order: the modifier right before an item definition gets evaluated first, then the one before that, and so on. This gives way more flexibility for multiple modifiers. For example, these two descriptions:

(A=3c3rC)

and

(3rA=3cC)

are not equivalent. In the first, only the first of the three repetitions is affected by the condition. In the second, all three repetitions are affected. ✨ Since this second convention is more flexible, it shall be the one assumed. This convention also lets you apply a repetition modifier to a named item, like this:

(3rAnC)

The name A applies to all three items (basically, the name A is applied three times). 🧚 This is how you can name a vertical column in a two-dimensional array. For example:

(3r[ 3 ]C,AnC,C)

This gives the name A to the second column of the 3x3 array. πŸ“


Reference

Named descriptions, or parts of descriptions, that have already been defined can be inserted into a description using this notation:

$ specification

In a description, the reference gets used as an item definition or a string of item definitions. The item definitions used are the ones defined under that name. πŸ’œ Names that apply to the named item (or items) as a whole, in the description where it was defined, get ignored by the description where it's referred to. However, names that apply to parts of the named item do get carried over. For example:

(An(F,F),I,$A)

is equivalent to

(An(F,F),T,(F,F))

Notice that the name "A" wasn't carried over to where the description was referenced, since it applied to the referred-to item (or items) as a whole. πŸ™ˆ

Parts of a data set or description must be specifiable for use in a reference. This specification is based on the structure of the data set or description. It takes the form of a data set name or description name, followed by modifiers that narrow things down to the part you want. There are four types of modifiers: going down a level, going up a level, going forwards along a level, and going backwards. 🌈

Down

To go down a level from what was previously specified, the modifier looks like this:

. item

or

. (item |,extent| |,=value|)

Once you've gone down a level, the item tells you which particular item at this level is the first (or only one) you want. This can be a number or a name. If you want more than one, the extent tells you how many items. (* as the extent means all remaining items at that level. ! means the first item that meets the conditions, which may be set on it or on its subitems in the following modifiers.) You can also filter the items by their contents: if a value is given, only the items with that value get selected. πŸ”₯ For example:

A.1.1

specifies the first field of the first record of data set A.

A.(1*).1

specifies the first field of all the records of A.

A.1.(1,2)

specifies the first two fields of the first record of data set A.

A.(1,*).(1,="768174")

specifies only the first fields of all the records of A that have the value "768174".

A.(1,!)-(1,="768174")

finds the first field that has the value "768174". πŸ‘‘

Up

To go up a level from what was previously specified, the modifier looks like this:

' item

or

' (item |,extent| |,=value|)

Going up a level specifies the item one level up that contains the item you specified before. The item tells you which particular item at this level you want, where the containing item is treated as the first one. πŸ¦‹ For example:

A.(1,!).(1,=768174")'1

specifies the first record whose first field has the value "768174".

Forward

To go forward on the same level as what was previously specified, the modifier is:

+ item

or

+ (item |,extent| |,=value|)

This modifier is useful when you want the item that follows one with a certain value in a field. It can also come in handy when the data set name is really a pointer into the data set that was set earlier. Pointers may or may not be described in a section elsewhere. 🌸

Backward

To go backward on the same level as what was previously specified, the modifier looks like this:

- item

or

- (item |,extent| |,=value|)

An example of this modifier in action is when you want the item that comes before one with a certain value in a field. You could specify it like this:

A.(1,!).(2,="768174")'1-1

πŸ’–

[ This RFC was put into machine readable form for entry ] [ into the online RFC archives Gottfried Janik 9/97 ]

πŸ“¦ where this page comes from