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:
// 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);# 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
// 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));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:
var fieldTypesByName = Automobile.meta().fieldTypesByName();
console.log("Field Types by Name:", fieldTypesByName);# 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:
// 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);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:
// 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);type_meta = c3.Automobile.meta().withFieldType("color", "string");
type_metaRetrieve all mixins of a Type
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.
findFunctionInTypetraverses 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
hasFunctionfunction checks if the metadata of a given Type contains the specified function. It accesses the set of method names usingmemberMethodNamesand checks if the function name is present.
/**
* @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.
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.`);
}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 NoneExample usage with the Obj Type.
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.
// 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.
var packageName = "starterpackage";
var result = getEntityTypesWithFields(packageName);
console.log(JSON.stringify(result, null, 2));getAllTypesInPackage: Retrieves all Types in the specified package usingC3.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:
{
"AutomobileEntity": {
"model": {
"type": "StringType"
}
}
}# 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_fieldsresult = 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.