Thursday, September 14, 2006

Data Typing Notes - Part 2

Operations on Core Data Types

Before moving on to the interesting part - application of operations on composite data types - let's take stock of what we already know about how operations are applied to core data types in programming languages.

A built in operation may be applied to core data type values using one of three common formats:

  • Binary operator
  • Unary operator
  • Assignment operator
  • Function call

We generally see the binary operator format in math expressions like this:

x = 2 + 8

It is rarely done, but we could rephrase the above statement using the function call format:

x = add( 2, 8 )

Generically, these formats are:

x = a OP_SYMBOL b
x = OP_NAME( a, b )

where a, b are the values and x is the result.

Finally, we have a couple examples of the unary operator format:

++c
-d

Again, we can restate both examples in the function call format:

c = increment( c )
x = negate( d )

In addition to the obvious fact that each function has only one input value, we observe that the first example changes the input value while the second does not.

The assignment format modifies the actual value
x *= 2
x OP_ASSIGN a
x = OP_ASSIGN_NAME( x, a )

Okay, let's set those observations aside and take a look at a composite data type and see what kind of trouble we can get ourselves into.

Operations on Composite Data Types

Whenever we create a new composite data type, no matter how simple, we need to review all of the standard operators and consider what functions make sense on the new type. Consider an application where you need to count how many times something occurs. Maybe we have an automotive application and we need to count how many lubrication cycles occur within a given time frame. This sounds like a job for an integer, but not quite. What we need is a counter. It can't ever be negative. Operations-wise we want to increment it, decrement it and reset it to zero. Let's define it.

We use a hierarchy format:

Layer : Counter
count : int[0..max]

So we have a data type structure named "Counter". To define our operations we use the OOP mechanism of inheriting the operations already defined in the integer base type with some overriding features. Specifically we want to use the ++ and -- unary operators, but we need to make sure that -- never takes us past 0. Additionally, we need a reset function. Rather than use a hard to read/remember operator we can stick to a function name.

Counter::
op(--) :
if --count < 0, count = 0
op(*) : not defined
op (-) : not defined
op (+) : not defined
reset () : count = 0

Please don't pick apart the syntax above. I'm not trying to nail that down yet! We're really just focusing on the required data. The example above shows that unless otherwise specified, all int operators and functions apply. We show that -- works differently. Some action language will be written in a function to be attached to the -- operator to override normal -- behavior. We turn off the *, - and + operators. We could just leave them on but let's say that we want to tightly define counter behavior. It's a counter and nothing else! A new function is defined for reset. All of this is pretty standard OO inherit and override behavior except that Counter is not an object or a class - it's just a data type in * UML. The distinction will be important and this IS different than OO programming languages.

The binary operation takes two inputs and returns a result without changing either input, so we need to specify a little more:

Compass_Heading::
op(+) :
sum = a + b
if sum == 0:
return 0
else:
return sum % 360
op(-) :
# insert action language here
op(*) : not defined

In the above psuedo-syntax we use the letters a and b to represent the symbols left and right of the operator. The result to be assigned to the LHS of the expression is "returned". By the way, I am imagining a GUI that displays all operators defined for the core type as check boxes that can easily be flipped to inherit_none or inherit_all. Then the modeler can click the boxes that are exceptions to the rule (inherit or don't inherit). To preclude weird analysis bugs, it is just as important to undefine operators as it is to extend them!

I just had a conversation with my colleague and good friend Andrew Mangogna and realize that I'm starting to confuse my terms regarding hierarchies, layers, matricies, etc. I am going to get this all sorted out by the next post.

No comments: