C3 AI Documentation Home

Type Introspection in the C3 AI Type System

Type introspection allows you to examine the properties, methods, and metadata of objects at runtime. Metadata provides detailed information about a Type's structure, including its fields, methods, mixins, and annotations. This helps developers understand the full capabilities and constraints of a Type. This is crucial for understanding the structure and capabilities of a Type, especially when dealing with complex models or dynamically generated Types.

Using the Type#meta function on a Type, you can retrieve metadata about a Type. Here's how you can use it:

JavaScript
// Retrieve the name of the Type
var typeName = Automobile.meta().name;
console.log("Type Name:", typeName);

// Check if the Type mixes in Obj
var isObj = Automobile.meta().isA(Obj);
console.log("Is Automobile an Obj:", isObj);

// Get the list of all fields defined in the Type
var fieldNames = Automobile.meta().fieldTypeNames();
console.log("Field Names:", fieldNames);
Python
# Retrieve the name of the Type
c3.Automobile.meta().name

# Check if the Type mixes in Obj
c3.Automobile.meta().isA(c3.Obj)

# Get the list of all fields defined in the Type
c3.Automobile.meta().fieldTypeNames()

Checking inheritance and mixins

JavaScript
// Check if the Automobile Type extends another Type
var isExtending = Automobile.meta().isExtension();
console.log("Is Automobile an extension Type:", isExtending);

// Get the base Type if it extends another Type
if (isExtending) {
  var baseType = Automobile.meta().baseType(true);
  console.log("Base Type:", baseType.meta().name);
}

// Get the list of mixins for the Type
var mixins = Automobile.meta().mixins();
console.log("Mixins:", mixins.map(m => m.meta().name));
Python
is_extending = c3.Automobile.meta().isExtension()

if is_extending == True:
    c3.Automobile.meta().baseType(true)

c3.Automobile.meta().mixins()

Retrieving field metadata

Get a map of all field Types by name:

JavaScript
var fieldTypesByName = Automobile.meta().fieldTypesByName();
console.log("Field Types by Name:", fieldTypesByName);
Python
# Get a map of all field Types by name
c3.Automobile.meta().fieldTypesByName()

The code snippet above returns an empty object since our Type has no fields.

Using dot notation to chain methods

Chaining methods using dot notation allows for more readable and expressive code. Here is an example of chaining methods to get specific metadata:

JavaScript
// Check if Automobile is a sub-Type of Obj and get its name
var isObjTypeName = Automobile.meta().isA(Obj) && Automobile.meta().name;
console.log("Automobile is Obj Type and its name is:", isObjTypeName);

// Get the names of all fields and check if they have defaults
var fieldsWithDefaults = Automobile.meta().dataFieldTypeNames().map(field => field);
console.log("Fields with defaults:", fieldsWithDefaults);
Python
for key in c3.Automobile.meta().dataFieldTypeNames():
    print(key)

Dynamic Type introspection

For dynamically created Types or anonymous Types, introspection helps to understand their structure and capabilities:

JavaScript
// Create a dynamic Type with an additional field
var dynamicAutoMeta = Automobile.meta().withFieldType("color", "string");

// Get the metadata of the dynamic Type
var dynamicTypeName = dynamicAutoMeta.name;
console.log("Dynamic Type Name:", dynamicTypeName);

// Get the field names of the dynamic Type
var dynamicFieldNames = dynamicAutoMeta.fieldTypeNames();
console.log("Dynamic Field Names:", dynamicFieldNames);
Python
type_meta = c3.Automobile.meta().withFieldType("color", "string");
type_meta

Retrieve all mixins of a Type

JavaScript
c3Grid(Obj.meta().mixins());

Traverse the Type hierarchy

The example below traverses the Type hierarchy by starting with the given Type, checking its metadata for the function, and then moving to its base Type until it either finds the function or reaches the top of the hierarchy.

  • findFunctionInType traverses the hierarchy of Types starting from a given Type and searches for a specific function. This is particularly useful in understanding how Types are structured and how functions are inherited or mixed in within the Type System.

  • The hasFunction function checks if the metadata of a given Type contains the specified function. It accesses the set of method names using memberMethodNames and checks if the function name is present.

JavaScript
/**
 * @param {Object} type - The initial Type to start the search from.
 * @param {string} functionName - The name of the function to search for.
 * @returns {Object|null} - The Type that contains the function or null if not found.
 */
function findFunctionInType(type, functionName) {
    /**
     * Checks if the given Type metadata contains the specified function.
     * 
     * @param {Object} typeMeta - The metadata of the Type to check.
     * @param {string} functionName - The name of the function to check for.
     * @returns {boolean} - True if the function exists, otherwise false.
     */
    function hasFunction(typeMeta, functionName) {
        // Ensure typeMeta is valid and memberMethodNames is a function
        if (!typeMeta || typeof typeMeta.memberMethodNames !== 'function') {
            return false;
        }
        // Get the set of member method names
        let methodNames = typeMeta.memberMethodNames();
        // Check if the set contains the specified function name
        return methodNames.has(functionName);
    }

    // Start with the provided Type and traverse the hierarchy
    let currentType = type;
    while (currentType) {
        // Get the metadata of the current Type
        let typeMeta = currentType.meta();
        // Check if the current Type has the specified function
        if (hasFunction(typeMeta, functionName)) {
            return currentType;
        }
        // Move to the base Type in the hierarchy
        currentType = typeMeta.baseType(false);
    }

    // Function not found in the Type hierarchy
    return null;
}

Example usage with the Obj Type.

JavaScript
let functionName = "type";
let typeWithFunction = findFunctionInType(Obj, functionName);

if (typeWithFunction) {
    console.log(`Function '${functionName}' found in Type: ${typeWithFunction.meta().name}`);
} else {
    console.log(`Function '${functionName}' not found in the Type hierarchy.`);
}
Python
def findFunctionInType(type_obj, function_name):
    """
    Find the Type in the hierarchy that contains the specified function.

    :param type_obj: The initial Type to start the search from.
    :param function_name: The name of the function to search for.
    :return: The Type that contains the function or None if not found.
    """
    def has_function(type_meta, function_name):
        """
        Check if the given Type metadata contains the specified function.

        :param type_meta: The metadata of the Type to check.
        :param function_name: The name of the function to check for.
        :return: True if the function exists, otherwise False.
        """
        # Ensure type_meta is valid and memberMethodNames is callable
        if not type_meta or not callable(type_meta.memberMethodNames):
            return False
        # Get the set of member method names
        method_names = type_meta.memberMethodNames()
        # Check if the set contains the specified function name
        return function_name in method_names

    # Start with the provided Type and traverse the hierarchy
    current_type = type_obj
    while current_type:
        # Get the metadata of the current Type
        type_meta = current_type.meta()
        # Check if the current Type has the specified function
        if has_function(type_meta, function_name):
            return current_type
        # Move to the base Type in the hierarchy
        current_type = type_meta.baseType(False)

    # Function not found in the Type hierarchy
    return None

Example usage with the Obj Type.

Python
function_name = "someMethod"
result_type = findFunctionInType(c3.Automobile, function_name)

if result_type:
    print(f"Function '{function_name}' found in Type: {result_type.meta().name}")
else:
    print(f"Function '{function_name}' not found in the Type hierarchy.")

Retrieving entity Types and their field metadata

This example demonstrate how to interact with and retrieve metadata about entity Types within a specified package using the C3 AI Type System. By obtaining all Types in a package and filtering them to identify entity Types, the example collects and returns detailed metadata about the entity Type's fields. This example helps you learn how to navigate the Type hierarchy, access metadata, and understand the structure and relationships of different Types.

JavaScript
// Retrieve all Types in the specified package 
function getAllTypesInPackage(pkg) {
    return C3.pkg().ownTypes(true);
}

// Function to filter entity Types from a list of Type metas 
function getEntityTypes(typeMetas) {
    // Use map and filter to streamline the filtering process 
    return Array.from(typeMetas).reduce((entityTypes, [name, typeMeta]) => {
        if (typeMeta.meta().isEntity()) {
            entityTypes.push({ name, typeMeta });
        }
        return entityTypes;
    }, []);
}

// Function to get entity Types and their fields as a JSON object 
function getEntityTypesWithFields(pkg) {
    // Retrieve all Types in the specified package 
    var typesInPackage = getAllTypesInPackage(pkg);
    // Check if the package has no Types 
    if (typesInPackage.size === 0) {
        return "The package has no Types in it.";
    }
    // Filter the Types to get only entity Types 
    var entityTypes = getEntityTypes(typesInPackage);
    // Check if the package has no entity Types 
    if (entityTypes.length === 0) {
        return "The package has no entity Types in it.";
    }
    // Initialize an empty object to hold entity Types and their fields 
    var entityTypesWithFields = {};
    // Iterate over the entity Types 
    entityTypes.forEach(({ name, typeMeta }) => {
        // Retrieve the declared fieldTypes for the current entity Type 
        var fieldTypes = typeMeta.meta().declaredFieldTypes;
        var fields = {};
        // Iterate over the fieldTypes and add to the fields object 
        fieldTypes.forEach(f => {
            fields[f.name] = {
                "type": f.valueType.type().name()
            };
        });
        // Add the fields to the entityTypesWithFields object 
        entityTypesWithFields[name] = fields;
    });
    // Return the JSON object with entity Types and their fields 
    return entityTypesWithFields;
}

Example usage with package name starterpackage.

JavaScript
var packageName = "starterpackage";
var result = getEntityTypesWithFields(packageName);
console.log(JSON.stringify(result, null, 2));
  • getAllTypesInPackage: Retrieves all Types in the specified package using C3.pkg().ownTypes(true).

    • Input: The name of your package.
    • Output: Map of all Types in the package.
  • getEntityTypes: Filters the list of Types to return only entity Types by checking if each Type's metadata indicates it is an entity.

    • Input: Map of Types.
    • Output: Array of entity Types with their names and metadata.
  • getEntityTypesWithFields: Combines the previous two functions to get all entity Types in a package and then iterates over each entity Type to collect metadata about its fields.

    • Input: Package name.
    • Output: JSON object where each key is an entity Type's name, and the value is an object containing the entity's fields and their Types.

The output for the starterpackage package is:

JSON
{
  "AutomobileEntity": {
    "model": {
      "type": "StringType"
    }
  }
}
Python
# Function to retrieve all Types in the specified package
def get_all_types_in_package():
    return c3.pkg().ownTypes(True)

# Function to filter entity Types from a list of Type metas
def get_entity_types(type_metas):
    entity_types = []
    #for x in type_metas:
    #    print(type_metas[x].meta().isEntity())
        
    for i in type_metas:
        if type_metas[i].meta().isEntity():
            entity_types.append({
                "name": type_metas[i].meta().name,
                "typeMeta": type_metas[i]
            })
    return entity_types

# Function to get entity Types and their fields as a JSON object
def get_entity_types_with_fields():
    # Retrieve all Types in the specified package
    types_in_package = get_all_types_in_package()

    # Check if the package has no Types
    if len(types_in_package) == 0:
        return "The package has no Types in it."

    # Filter the Types to get only entity Types
    entity_types = get_entity_types(types_in_package)
    print(entity_types)

    # Check if the package has no entity Types
    if len(entity_types) == 0:
        return "The package has no entity Types in it."
    
    # Initialize an empty dictionary to hold entity Types and their fields
    entity_types_with_fields = {}

    # Iterate over the entity Types
    for entity in entity_types:
        name = entity["name"]
        type_meta = entity["typeMeta"]
        
        # Retrieve the declared fieldTypes for the current entity Type
        field_types = type_meta.meta().declaredFieldTypes
        
        # Initialize an empty dictionary to hold the fields of the current entity Type
        fields = {}
        
        # Iterate over the fieldTypes and add to the fields dictionary
        for field in field_types:
            fields[field.name] = {
                "type": field.valueType.type().name()
            }
        
        # Add the fields to the entity_types_with_fields dictionary
        entity_types_with_fields[name] = fields
    
    # Return the dictionary with entity Types and their fields
    return entity_types_with_fields
Python
result = get_entity_types_with_fields()
print(json.dumps(result, indent=4))

Using the console to explore the Type System API documentation provides essential insights into the language's syntax, features, and best practices, enabling you to effectively utilize the Types.

Was this page helpful?