Lesson 10.2: Function chaining

Simple function chaining

Much like views in a relational database, which can be generated from other views, functions in TypeDB can leverage answers generated by other functions.

fun verified-reviews($book: book) -> { review }:
  match
  $review isa review;
  rating ($review, $book);
  order-line ($order, $book);
  $_ isa action-execution ($user, $review), has timestamp $review-time;
  $_ isa action-execution ($user, $order), has timestamp $order-time;
  $review-time > $order-time;
  return { $review };
fun unverified-reviews($book: book) -> { review }:
  match
  $review isa review;
  rating ($review, $book);
  not {
    let $verified-review in verified-reviews($book);
    $review is $verified-review;
  };
  return { $review };

The unverified-reviews function can re-use the work of the first function, modularizing logic effectively and promoting re-use.

Recursive function chaining

Functions can recurse as well! A recursive function is one which invokes itself.

fun transitive_places_unoptimized($place: place) -> { place }:
  match
    {
      locating (located: $place, location: $transitive);
    } or {
      locating (located: $place, location: $next);
      let $transitive in transitive_places_unoptimized($next);
    };
  return { $transitive };

Note that this function is written in the unoptimized recursive form. Read the Optimizing queries page to understand how to write this more efficiently.

match
$user isa user, has id "u0003";
(located: $user, location: $place) isa locating;
fetch {
  "direct-place": $place.name,
  "transitive-places": [
    match let $transitive in transitive_places_unoptimized($place);
    fetch { "place": $transitive.name };
  ],
};
{
  "direct-place": "Newark",
  "transitive-places": [
    {
      "place": "United States"
    },
    {
      "place": "New Jersey"
    }
  ]
}

Recursive functions have "self-terminating" behaviours that are guaranteed to reach a fixed point and finish their computation, under the condition that the function isn’t continuously generating new values (or in the future - lists).

In other words, when using functions to simply modularize queries and invoke recursion without computing new data, functions will always terminate - even in the case of data cycles. Beware that passing an incrementing or decrementing integer argument does invalidate this guarantee - which might be what you want!