[SparForte][Banner]
[Top Main Menu] Intro | Tutorials | Reference | Packages | Examples | Contributors   [Back Page]      [Next Page]  

Script Tutorial 3: Data Types

If you used other scripting languages, you may find SparForte's type system confusing. In most languages, a type is a primitive representation of data, like an integer or float. Any new types must be built up from object classes.

The type system is used for much more than describing the physical representation of data. It can be used to construct abstractions that represent values from the real world and describe how they relate to each other. In this way, you work at a "higher level", working with concepts and not thinking as much about the physical characteristics of a date type. Using the type system correctly is important for effectively writing quality software.

Every Value Must Have a Type

As you saw in the earlier tutorials, every value has a data type. Every variable or subprogram parameter must be declared with its type. This means that SparForte is a language with static typing (although it has some dynamic typing features which will be described below). At the command prompt, you can assign values to new variable names and SparForte will automatically declare variables and give them the same data type as the expression. The type can be changed with the typeset command, or even deleted with the unset command. These features only work on the command prompt.

In a script, you will have to declare all variables yourself and give them appropriate data types. You can't change the type of a variable nor can you destroy a variable. When a script runs, SparForte performs type checking before any commands execute, catching type errors early.

SparForte is also a strongly-type language, which means that there are restrictions for how different types of data can be mixed. Most strong typing languages will not allow strings to be mixed with numbers in an expression (they are treated as incompatible). However, SparForte uses more strong checking than this, and even structurally equivalent data values may be designated as incompatible in some circumstances.

Creating New Types

New data types can be created using two different statements. The type statement creates a new type derived from an existing type. The subtype statement creates a renaming of an existing type. Using type and subtype, you can create a hierarchy or tree of types without using object classes.

With a new type (created with the type statement), the new type and the parent type are structurally equivalent. You can create any number of new types derived from the parent type but each new type is incompatible with its sibling or its parent types. Although they are all structurally the same, the are logically incompatible.

For example, you can create two new float-based types to represent weight and distance.

type distance_km is new float;
type weight_kg is new float;

Both distance_km and weight_kg are both types of floats and have the same properties as floats. Any function parameter that expects a float will also accept these new types. But they are incompatible with each other: a distance_km value cannot be assigned to a weight_kg, and a weight_kg function parameter will not accept a distance_km value. Nor can you use either of these new types with float function parameter.

They can be used in situations such as:

  • database id numbers
  • different units of measurement
  • different characteristics
  • validated and unvalidated web application data
  • encoded and unencoded data

The type system is static, which means the types are usually checked before the script is run. If there are type incompatibilities, they will be discovered before anything executes.

Creating Subtypes

The subtype statement works much the same way as the type statement except that a subtype acts like a renaming of the existing type. The new type and the parent type are structurally equivalent. You can create any number of new types derived from the parent type and each new type is compatible with its sibling type. They can all be used interchangeably.

For example, you can create a shorthand name for a type with a long name for an integer-based type like this.

type customer_id is new integer;
subtype cid is customer_id;

cid is an integer type that is a renaming of customer_id and can be used any place that customer_id is used. A customer_id value can be assigned cid variable, or the other way around.

Type Casting

Any two structurally equivalent types can be explicitly treated the same by using a type name as a function. This is called type casting. Use type casting to override type incompatibilities.

For example, suppose you have two types, one for HTML encoded strings and one for unencoded strings.

type html_encoded_string is new string;
type html_unencoded_string is new string;
...
title : constant html_unencoded_string := "My Page Title";
web_page : html_encoded_string;

If you know that the title variable contains data that doesn't need to be HTML encoded, you can add it to the web_page by converting it to an html_encoded_string value using type casting.

web_page := web_page & html_encoded_string( title );

In SparForte, the positive and natural types are treated specially. These are subranges of the integer type. Positive values are always compatible with natural values, and natural values are always compatible with integer values. But natural values cannot be assigned to a positive, for example, because the range of possible values for a natural is larger than that of the positive type.

When to Use Types and Subtypes

There are different opinions for how to use the type system.

Some developers believe that the primitive, predefined types should only be used to create new types and, in your program, your variables and parameters should only use types that you have created.

Some developers are concerned that too many types can be cumbersome, requiring extra type casting, converting one type to another. For example, if you have a lot of different integer types and you want to use them in an expression, you may have to typecast them all to integer and then typecast the result back to the final type, which makes the expression hard to read.

It is important that you use new types where they make the most sense. If a customer id and a supplier id will never be combined in an expression, then it makes good sense to define them as incompatible types.

Predefined Types

There are many predefined types. More types are declared in the built-in packages. These are described in detail in the Reference section on types. The following type tree shows most of the predefined types and how they are related to one another.

[SparForte Type Tree]

Universal Types

As mentioned in the other tutorials, SparForte also has universal types. These are the most primitive types and are the base types of most of the other predefined types. A universal type is special in that it is automatically compatible with any type of the same structure. These are intended for short, simple scripts or working on the command line when you don't want to take advantage of SparForte's typing system.

There are three universal types.

  • universal_typeless is the root type of string and numeric types. It will be treated as a string or number depending on context. If the context is ambiguous, it will be treated as a string.
  • universal_string is the root type of all strings and characters. It automatically matches any string or character type.
  • universal_numeric is the root type of all number types. It automatically matches any integer, float or other number type.

Because these types are root types, if you create a new type using type or subtype, the new type will not have the special properties of the universal type. This shouldn't be much of an obstacle, since you use the universal types to avoid the type system.

These types are similar to weak typing: an universal_typeless variable is always contains a universal_typeless value, but the value is interpreted as a string or integer depending on the expression or context, implicitly type casting the value. This is different from dynamic typing, where the type of a variable is unknown until the program runs.

There is a trade-off when using universal types. They can be more convenient when creating a quick prototype or a short script, but you also lose the safety, readability and easy scaling advantages of explicit types and compile-time error checking. Like any data type, be cautious and chose the approprite type for your task.

Types, Subtypes and Aggregate Types

Another use for the type system is to construct aggregate types such as arrays or records.

Once you define a new aggregate type, you can create derived types using the type and subtype statements just like any other type.

[Right Submenu]

 Command Prompt Tutorial 1: SparForte as a Calculator

 Command Prompt Tutorial 2: Basic Shell Commands

 Command Prompt Tutorial 3: Working with Databases

 Script Tutorial 1: Basic Commands Scripts

 Script Tutorial 2: Intermediate Program Scripts

 Script Tutorial 3: Data Types

 Template Tutorial 1: Basic Templates

 Template Tutorial 2: Intermediate Templates

 GCC Tutorial: Compiling SparForte Scripts

 Debugging Tutorial - Using the SparForte Debugger

 Creating a Profile Script

 Calling SparForte from C: A Tutorial

 SparForte For PHP Developers

 SparForte Best Practices

[Back to Top] Back To Top [Small Forte Symbol]