Showing posts with label metamodel. Show all posts
Showing posts with label metamodel. Show all posts

Tuesday, September 19, 2006

Action Language Metamodel

I've been pondering the action language metamodel the last couple of days. For reseach, I've printed out the in-depth SMALL paper from back in 97 and xeroxed the two page summary from Executable UML as well as my SCRALL notes.

Goals of this action language are:
  1. Emphasize parallel processing and data flows.
  2. Ensure that the language is as easy to read as the class and state models.
  3. Support a full range of data types and domain bridge mechanisms.
I'm really impressed with the quality of the SMALL paper. I will probably just implement it straight out. But how?

I certainly need to model action behaviors (link, create, write attribute, etc.) as primitive operations in the class and state metamodel subystems. For now I will just imagine these as domain functions with appropriate signatures. Example:

::Unlink(object_id1, object_id2, rnum)

Clearly we need a lex-yacc generated parser, which means I need to write up a full grammar. I'm thinking that the intermediate representation produced by the parser will populate an action language metamodel - yet to be built.

So when action language is entered, it will be parsed and the action language metamodel will be populated. When a block of action language is triggered at run-time, the action language metamodel will invoke the primitive operations with appropriate parameter values.

The lex-parser and primitive ops are the easy parts, so I will save those for last. I hope to post progress here in the coming days.

It's a nice day for a walk so I'm heading to the local cafe to ponder further.

Sunday, September 17, 2006

First Data Type Class Model

The good news is that I've sorted out some terminology since the last post (as promised!). The bad news is that I've clearly got some more studying to do before nailing this data type thing down the way I want. I suppose, though, it's good news that I've figured that much out. After my conversation with Andrew, I'm concerned that this subsystem could turn into a serious project of its own. So we will probably limp along with a miminal data typing system whilst the rest of the action language gets sorted out.

For now, I propose the following model be inserted into the datatype subsystem of the * UML metamodel.


If you don't have an image resizer plugin (firefox) the diagrams might be hard to read. I'm new to the blog - writely technology and haven't figured out the easiest way to insert readable model fragments. I will post the full pdf and BridgePoint files on the sourceforge site, though. Probably in a separate file package.

Here I just want to excerpt and explain what I have so far. This is only a model of data type structure, not data type operations. The model is best explained in two pieces: core and user data types. Then I will construct the Position data type to show how the model works.

Core Data Types

First let's examine the left side - the core data types.

Nothing mysterious here. I have superclassed the Real and Integer Numeric types because they each may specify units.

Note that the max/min values on Integer and Real are kept in the subclasses since the types are not the same. The real type also specifies a precision as a positive integer representing decimal places to the right.

The Enumerated data type consists of a set of Symbols, each of which has a Value.

Finally, String has a positive integer maximum length. Now let's flip over to the right side.

User Data Types



A Vector is simply a collection of one or more values (as determined by Vector.Scale) all specified by the same Datatype. Since the Datatype can be either User or Core we can set up a hierarchical structure. Since Vector.Scale can be 1 or more we can build an N-dimensional matrix at any level of the hierarchy. Let's review the examples mentioned in my earlier post.

First we'll work from the bottom up to define the Position data type. Since minutes and seconds are the same in both latitude and longitude, let's define those.

Minutes

Our specification for Minutes is:
Name: Minutes
Core Type: Integer
Min_value: 0
Max_value: 59
Units: minutes

So this gives us an instance each of: Integer, Numeric, Core Data Type and Data Type

Seconds

Using a more compact syntax, we specify:
Seconds: real[0..60] prec 2 units seconds

This gives us an instance each of: Real, Numeric, Core Data Type and Data Type

Degrees Latitude

Lat_Degrees: int[-90..90] units degrees

Degrees Longitude

Long_Degrees: int[-180..180] units degrees

Unlike thte earlier post I am using negative degrees to avoid defining an enumerated type for [N | S] and [W | E].
No particular reason - just lazy. Also the max Minutes is 59 while max Seconds is 60 (59.999...). Seems like it is better to say "60". Otherwise you end up redundantly specifying the precision with a sequence of trailing nine's.

Latitude and Longitude

Now we start creating User Data Types as we work our way up the hierarchy.

Name: Latitude
Vector: name Degrees, scale 1, value_datatype Lat_Degrees
Vector: name Minutes, scale 1, value_datatype Minutes
Vector: name Seconds, scale 1, value_datatype Seconds

Name: Longitude
Vector: name Degrees, scale 1, value_datatype Long_Degrees
Vector: name Minutes, scale 1, value_datatype Minutes
Vector: name Seconds, scale 1, value_datatype Seconds

Position

Name: Position

Vector: name Latitude, scale 1, value_datatype Latitude

Vector: name Longitude, scale 1, value_datatype Longitude

Matrix example

Now if, for some reason, we wanted a 5x10 matrix of Position data we could do this:

Name: Position_Column

Vector: name Position, scale 10, value_datatype Position

Name: Position_Matrix

Vector: name Position_Rows, scale 5, value_datatype Position_Column

Moving on

Okay, that's it for now. I might shelve the data type subsystem for a few days while I get some other tasks under control. Namely: Publish up a project roadmap, resume work on the model build script parser, sketch out key parts of the action language subsystem.

Stay tuned!

- Leon

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.

Wednesday, September 13, 2006

Data Typing Notes

The term "data type" describes both a structure and a set of operations. Consider the integer data type. Transforming operations such as add, subtract and multiply yield a new integer value. Comparison operations like greater_than, less_than and is_equal_to yield a boolean result.


Analysts rely on two kinds of data types during model development. Core data types and composite data types. Core data types are built in to the modeling language. The analyst can create a composite data type using one or more core data types as building blocks.

What core data types should * UML support? Is the list of core data types bounded by some principle or can you just keep adding them? How are composite data types actually built? How much structure is allowed in a composite data type - is there such a thing as too much? How are composite data types handled by model compilers? How are they handled by an interpreter? These are just a few of the questions that must be answered to construct a useful * UML Data Type metamodel subsystem.

Core Data Types

Since a core data type is built into the modeling language it must be supported by any model compiler. Both the data type structure and all operations should be fully implemented. Keep in mind that a set of models might be translated to Ruby or Java on one day and then directly to Z80 assembler on another. To support all model compilers, we are safer to keep the range of core data types as limited as possible.

On the other hand, model developers like to have a wide variety of data types. In fact, real world applications rarely call for a core data type as seemingly ubiquitous as real. Consider an air traffic application. The compass heading data type is not real. It is in fact [0..360] degrees with a precision of, say, .01. The math operations aren't the same either for compass heading. 180.15 + 270.00 = 90.15. Similarly we need distinct data types for altitude, speed and so forth. True, each of these user data types can be tightly or loosely based on the real core data type, but each is different.

So we have a balancing act. To model application requirements independent of any implementation, we want to support lots of user (application level) data types. To support a wide range of model compilers and target languages, we must limit the set of core data types. And right there we have the answer to our problem.

Any user data type must be derivable from core data type building blocks. The model compilers will be concerned only with the core data types. But the modeler will have everything he or she needs to specify whatever application level data types the project demands. Before moving on to how the user builds composite data types, lets first consider the core set.

Consider these proposed core data types:

  • boolean
  • integer
  • real
  • enumerated
  • string

The first two types, boolean and integer, are supported in every programming language down to assembly level (as far as I know). So they are safe bet. At the level of C and upward, the real (float) data type is supported. Some embedded processors lack floating point capability. But floating point math is quite common in real world applications. In the worst case, a model compiler will have to convert all read core types into integer types during compilation.

Now we move on to enumerated. This is easily implemented using integers so we easily demand this capability of any model compiler.

What about the string data type? C doesn't support strings directly - why should * UML? The data structure is easy enough to accommodate, but there are a number of string operations that must be supported. But strings are extremely common in real world applications. It's hard to imagine avoiding them. That said, certain embedded targets have resource limits that preclude the liberal use of string data types. A model compiler might need to convert string values into a concatenated or compressed integer form to acheive any necessary economy. Since I'm an analyst I have to say "enough for me, let's shift the burden onto the model compiler!" We need strings!

But that's it - we're drawing the line at string. You want to build a model compiler? These are the only types you need to handle. I now assert that the modeler has enough building blocks to construct any required user data type.

Composite Data Types

To build a composite data type we create a Type Specification using one or both of the following layout patterns:

  • Hierarchy
  • Matrix

Hierarchy Layout

Let's say that we want to create the application level Position data type. Hang on - let me bring up Google Earth so I can make sure I get this right... Okay, the Golden Gate Bridge is here:

37 48' 59.41" N
122 28' 54.05" W

We create a Layer called "Latitude" with four data Cells. The cells are ordered sequentially as:

Layer : Latitude
degrees: int[0..90]
minutes: int[0..60]
seconds: real[0..60] prec 2
pole: enum( N | S )

Each cell refers to a core data type. Each core data type has its own qualifying characteristics.

Similarly we create another Layer called "Longitude":

Layer: Longitude
degrees: int[0..180]
minutes: int[0..60]
seconds: real[0..60] prec 2
meridian: enum( W | E )

It is clear we could have created a lower layer to contain minutes and seconds since they are identical, but let's not worry about that now. We create a top layer for Position and we have a complete Type Specification.

Layer: Position
Latitude
Longitude

It seems that composite data types don't require qualifying characteristics. Is this true? Hmmm.

Matrix Layout

We can also arrange core data type cells in a matrix pattern. To specify a 10x10x2 3D matrix of integers between 1 and 100 where any value is at (x, y, z) we could do this:


Matrix: int[1..100]
x: 10
y: 10
z: 2

Ah, but what if we wanted to create a 1x5 matrix of Position values for some reason? Then we combine both the Matrix and Hierarchy layout patterns.


Matrix: Position

p: 5

Composite Data Structure Summary

The Hierarchy and Matrix layout mechanisms make it possible to construct composite data types. But it's not enough to define a data type. We need to somehow define the operations specific to that data type.


But I need to get a sandwich right now. So stay tuned for the next post where we will ponder transformation and comparision operators on composite data types and such things.


- Leon