Type Database
Overview
The type database is a feature of the Fantom runtime which automatically maintains a database of all the installed pods and types. The type database itself is stored in a binary file called "etc/sys/types.db" of the Env.workDir
. It is automatically rebuilt as needed if the runtime detects that the installed pods have been modified.
Facet Indexing
The most important job of the type database is to provide a mechanism to index facet name/value pairs to types. Facet indexing lets us build pluggable features without a bunch of complicated setup - a pod file gets dropped into the runtime, and any plugins are automatically registered.
For example, let's say we want to map file extensions to a fictional parser type. First we need to declare the facet symbol - in this case we will call it "parserForExt". Then we declare that the symbol should be indexed via the pod's @sys::podIndexFacets
facet:
@podIndexFacets = [@parserForExt] pod acme { Str[] parserForExt := Str[,] }
Now we annotate our types with the facet:
@parserForExt=["xml"] class XmlParser {} @parserForExt=["csv", "txt"] class CsvParser {}
Indexed facets can be any value type except a List. Lists are treated specially such that each value of the list is indexed separately. In the example cdoe above, we would index both "csv" and "txt" to CsvParser
.
The type database will automatically build an index so that we can efficiently query this facet:
// query all the types annotated with parserForExt="xml" parsers := Type.findByFacet("parserForExt", "xml") // query just one parser := Type.findByFacet("parserForExt", "txt").first
If we want to add more parsers, we only need to drop in new pods - the type database automatically takes care of everything else.
Type Relationships
A very common use case is creating late binding type-to-type relationships. For example let's say we want to declare an IntEditor
for Int
, EnumEditor
for Enums
, etc. We create these relationships using facet indexing, where the value of the facet is type literals:
@myEditor=Int# class IntEditor {} @myEditor=Enum# class EnumEditor {}
Often when performing a query, we want to use relationships established via the inheritance hierarchy. For example EnumEditor
is bound to the base Enum
class - we can use an optional argument to findByFacet
to search up the hierarchy:
// this line returns an empty list Type.findByFacet("myEditor", Month#) // this line returns [EnumEditor#] Type.findByFacet("myEditor", Month#, true)
Later we might to create a custom editor for Month
:
@myEditor=Month# class MonthEditor {}
Now when we query for month we will actually pick up both types:
// this line now returns [MonthEditor#, EnumEditor#] Type.findByFacet("myEditor", Month#, true)
But since the inheritance
hiearchy is searched in order, the most specified binding will always come first in the list:
// this line returns MonthEditor# Type.findByFacet("myEditor", Month#, true).first