Writing functions

Schema for the following examples
#!test[schema, commit]
define
  entity content owns id;
  entity page sub content,
    owns page-id,
    owns name;
  entity profile sub page,
    owns username;
  entity user sub profile,
    owns phone,
    owns karma;

  attribute id value string;
  attribute page-id sub id;
  attribute username sub page-id;
  attribute name value string;
  attribute phone value string;
  attribute karma value double;
# Some extra just to test the snippets
fun f1($a: integer) -> integer:
match let $x = $a;
return first $x;

fun f2($a: integer) -> integer:
match let $x = $a;
return first $x;

fun scalar_fun($a: integer, $b: integer) -> integer:
match let $x = $a + $b;
return first $x;

fun tuple_fun($a: integer, $b: integer) -> integer, integer:
match let $x = $a + $b; let $y = $a - $b;
return first $x, $y;

fun stream_fun($a: integer, $b: integer) -> { integer, integer }:
match let $x = $a + $b; let $y = $a - $b;
return { $x, $y };

Syntax

Declaring a function

Functions in TypeQL are declared using the keyword fun. A function declaration consists of:

  • A function signature which itself consists of:

    • A function name.

    • Typed input arguments (optional).

    • A return type.

  • A read pipeline as the function body.

  • A return statement that produces results of the specified return type.

Function Declaration Syntax
fun <function-name> ( <optional-arguments> ) -> <return-types>:
<read_pipeline>
return <return-statement>
  • <function-name> is a valid label (i.e. a not reserved keyword or a TypeQL literal).

  • <optional-arguments> is a list of zero of more typed variables, written in the format $a : A, $b : B, …​

  • <return-types> consists of at least one <return-type> and can be:

    • a stream tuple type, written in the format { A, B, …​ }.

    • a tuple type, written in the format A, B, …​.

    The types A, B, …​ above can be either user-defined types (e.g. an entity type user) or primitive value types (e.g. boolean).

  • <read_pipeline> includes any read pipeline.

  • <return-statement> may consist of variables, function calls, and built-in reductions:

    • Scalar return statements are of the form $a;.

    • Tuple return statements are of the form $a, $b, <built-in-reduction>, scalar_fun($c), …​; (see reduce for available built-in reductions).

    • Stream return statements are of the form { $a, $b, scalar_fun($c), …​ };.

    Positions matter! Returns of functions are assigned by position.

Calling a function

Function Calling Syntax
<function-name>(<arguments>)

<arguments> to a function call are of the form $a, $b, …​. They are always variables (and not expressions).

Function can be called:

  1. as part of an expression. For example:

    Scalar expression comparison
    #!test[read, count = 1]
    #{{
    match let $x = 0 ; let $y = 1;
    #}}
    $x + 1 < f1($y) + f2($y);
    Scalar expression assignment
    #!test[read, count = 1]
    #{{
    match let $a = 0 ; let $b = 1;
    #}}
    let $x = scalar_fun($a, $b) / 2;
  2. as part of a tuple or stream assignment, for example:

    Tuple assignment
    #!test[read, count = 1]
    #{{
    match let $a = 0 ; let $b = 1;
    #}}
    let $x, $y = tuple_fun($a, $b);
    Stream assignment
    #!test[read, count = 1]
    #{{
    match let $a = 0 ; let $b = 1;
    #}}
    let $x, $y in stream_fun($a, $b);

Usage

Ambiguity

Every function should have a unique name. It’s not allowed to declare two functions with the same name even if they have totally different arguments and return types.

Using Functions at Query-Level

Functions can be defined temporarily at the query level using the with clause, becoming a with pipeline stage. Such functions exist only for the duration of the query and are not persisted in the database.

Multiple with clauses can be used, each defining a single function. All with clauses must precede the query pipeline.

Querying with Functions
with <f1-declaration>
with <f2-declaration>
...
with <fN-declaration>
match
  $f1 = f1();
  $f2 = f2();
  ...
  $fN = fN();

For example, the following read pipeline defines and uses a temporary function using the with clause:

#!test[read]
with fun karma_with_squared_value($user: user) -> karma, double:
  match
    $user has karma $karma;
    let $karma-squared = $karma * $karma;
  return first $karma, $karma-squared;

match
  $user isa user, has username $name;
  let $karma, $karma-squared = karma_with_squared_value($user);
select $name, $karma-squared;

Using Functions in definition queries

Define

In addition to temporary query-level functions, functions can be persisted in schemas. Replace the keyword with with define and execute the query in a schema transaction:

#!test[schema, commit]
define
  fun karma_with_squared_value($user: user) -> karma, double:
    match
      $user has karma $karma;
      let $karma-squared = $karma * $karma;
    return first $karma, $karma-squared;

Once persisted, these functions can be called directly in read queries:

#!test[schema]
match
  $user isa user, has username $name;
  let $karma, $karma-squared = karma_with_squared_value($user);
select $name, $karma-squared;

Redefine

Everything except for the function name can be redefined using redefine queries.

#!test[schema]
redefine
  fun karma_with_squared_value($karma: karma) -> double:
    match
      let $karma-squared = $karma * $karma;
    return first $karma-squared;

Undefine

To undefine a function, specify the fun keyword and its name.

#!test[schema]
undefine
  fun karma_with_squared_value;

Learn More

Learn more about function bodies - pipelines.

Understand how to work with stream functions.

Explore single functions and their usage.