![]() |
![]() |
![]() |
||||||||||||||
| User-defined Functions < TMQL Introduction < < Home | ||||||||||||||||
|
User-defined FunctionsOnce queries get more complex, they have to be organized and engineered. One vehicle in this process is to compose functions. These can compute values from provided parameters and―semantically speaking―define a functional relationship between values and other things. We can declare a function in the meta map; there it is just a topic of a certain type: """ nr-albums isa tmql:function description: this function computes the number of albums a certain person has produced return: fn:length (// albums [ . <- opus -> author = $person ]) """The TMQL processor will register this topic as function then. Since it is a normal topic, we can attach all sorts of relevant (or irrelevant) information. One important part is the body, where we can use TMQL to define what should be returned. Here it is a one-liner, a path expression, but any TMQL style is admissible here. From the expression and the one free variable $person, the processor will also learn that there is one parameter person to that function. Inside the query we can make use of the function: select $p / name, nr-albums (person: $p) where $p isa personNote that we have to associate a current person (stored in $p) with the formal parameter person to passed it into the function. Any number of parameters can be introduced this way. What can also be done is to provide default values for function parameters, if the caller does not provide one. The following function top-albums finds the most popular albums for a given producer: """
top-albums isa tmql:function
description:
return: """
select $album
where
$album isa album &
is-produced-by (producer: $person, opus: $album)
order by $album / popularity desc
limit $limit || 5
"""
"""
While the function uses 3 different variables, only $person and
$limit are free i.e. not quantified by the query
itself. The expression $limit || 5 is only a shortcut for the more
elaborated if $limit then $limit else 5, it uses the parameter if that is
defined and uses 5 otherwise.
If the function is invoked with all parameters, these then will be used:
select $person / name, top-albums (person: $person, limit: 3) / name
where
$person isa person
As a consequence, we will receive a list of person names and album names; at most 3 albums for
one person. Alternatively, we can omit the limit, so that the default 5 is
used.
The fact that functions are topics, allows a further variation of the theme. A processor may allow a developer to use a language other than TMQL, say, Python: """
ctime isa tmql:function
return @ python: """
from datetime import ctime
return ctime()
"""
"""
All we needed to do was to set the scope accordingly. Needless to say, that this is an
excellent extension mechanism, but only if the function itself is short enough to be directly
included. For bigger external libraries it is probably more manageable if the source code
lives externally:
""" math isa tmql:ontology ~ file:/usr/local/math/api.atm """which brings us back to importing whole ontologies. The only difference to before is that the api.atm file contains the functions, all topics of type tmql:function: ... pow isa tmql:function ... sqrt isa tmql:function ...To use these functions inside the query, we have to prefix them: math:pow and math:sqrt. What if the functionality should or cannot be encoded in source code as part of a topic map? Also this case is covered as ontological import: """ math isa tmql:ontology ~ http://maths.is.great/ """As before, we used a URI to indicate the ontology. Unlike before, the implementation of the processor allows us to bind a, say, Python library to exactly this URI: import math
proc = TMQL.processor()
proc.registerNS ('http://maths.is.great/', math)
As soon as it recognizes the URI as one registered namespace, it will not try to dereference
the URI, but will rather load the math library. From then on, everything works as before.
And there is a last benefit for registering functions in the meta map: introspection. With it, it is possible to actually query the meta map itself to learn about the functions:
select $f / description
from %__
where
$f isa tmql:function
The special variable %__ not only names the meta map; inside a FROM clause
it also causes the processor to switch its focus on the meta map and to interpret everything
following relative to it. The rest is history, as they say.
|
|||||||||||||||