diff --git a/doc/userdoc.org b/doc/userdoc.org
index ba7cd798a6a7991d6451c405a5b9c81dfa2c0362..0ed26f5d8c495ab9b839e7fdf131c6cdc2f764d9 100644
--- a/doc/userdoc.org
+++ b/doc/userdoc.org
@@ -2160,6 +2160,10 @@ compound). The type of ~x~ is ~X~ and for lists, each argument ~xi~ belongs
 to ~Xi~. The function themselves are defined by the expressions ~e~ (or
 ~e1~, ..., ~em~) which types are set by ~Y~ (or ~Y1~, ..., ~Ym~).
 
+#+BEGIN_warning
+- ~X~, ~X1~, ..., ~Xn~ cannot be tuples spaces.
+- ~Y~, ~Y1~, ..., ~Ym~ can be tuple spaces.
+#+END_warning
 
 Let us give a few examples.
 #+NAME: R-to-R-function
@@ -2184,6 +2188,15 @@ returned type
 #+END_SRC
 #+results: R-to-R1-function
 
+But one cannot use tuples to define the domain
+#+NAME: no-tuple-in-domain
+#+BEGIN_SRC pugs-error :exports both :results output
+  let f: (N)->N, n -> 3;
+#+END_SRC
+produces the following compilation time error
+#+results: no-tuple-in-domain
+
+
 Using compound types as input and output, one can write
 #+NAME: R22-R-string-to-R-string-function
 #+BEGIN_SRC pugs :exports both :results output
@@ -2208,6 +2221,16 @@ Using compound types as input and output, one can write
 This meaningless example produces the following result.
 #+results: R22-R-string-to-R-string-function
 
+The following example shows how to use tuples as codomain.
+#+NAME: R-to-tuple-R-function
+#+BEGIN_SRC pugs :exports both :results output
+  let f: R -> (R), x -> (2*x, 2-x, 7*x-2, 2, x/3);
+
+  cout << "f(3.2) = " << f(3.2) << "\n";
+#+END_SRC
+#+results: R-to-tuple-R-function
+
+
 **** Lifetime of function arguments
 
 The arguments used to define a function are *local* variables that exist
@@ -2325,7 +2348,8 @@ produces the following compilation time error
 In ~pugs~ language, builtin functions are ~C++~ pieces of code that can be
 called in scripts. Their usage is very similar to user-defined
 functions. They differ from user-defined functions in three points.
-- Builtin functions may have no parameter or no returned value.
+- Builtin functions may have no returned value.
+- Builtin functions can use tuples as arguments.
 - Builtin functions are polymorphic. More precisely, this means that
   the signature of a builtin function is also defined by its expected
   argument types.
diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp
index 8ee9225cd75b76d6aff8b82a7f853cb74c40bacc..0f8f1d287b88435c904c00557e7fc0dc729dcb56 100644
--- a/src/language/ast/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp
@@ -175,6 +175,27 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
           throw ParseError(message.str(), parameters_domain_node.begin());
         }
 
+        {
+          if (parameters_domain_node.is_type<language::type_expression>()) {
+            for (size_t i_domain = 0; i_domain < parameters_domain_node.children.size(); ++i_domain) {
+              if (parameters_domain_node.children[i_domain]->is_type<language::tuple_type_specifier>()) {
+                std::ostringstream message;
+                message << "cannot use tuple " << rang::fgB::yellow
+                        << dataTypeName(parameters_domain_node.children[i_domain]->m_data_type) << rang::fg::reset
+                        << " as a domain for user functions" << rang::style::reset;
+                throw ParseError(message.str(), parameters_domain_node.children[i_domain]->begin());
+              }
+            }
+          } else {
+            if (parameters_domain_node.is_type<language::tuple_type_specifier>()) {
+              std::ostringstream message;
+              message << "cannot use tuple " << rang::fgB::yellow << dataTypeName(parameters_domain_node.m_data_type)
+                      << rang::fg::reset << " as a domain for user functions" << rang::style::reset;
+              throw ParseError(message.str(), parameters_domain_node.begin());
+            }
+          }
+        }
+
         auto simple_type_allocator = [&](const ASTNode& type_node, ASTNode& symbol_node) {
           Assert(symbol_node.is_type<language::name>());
 
@@ -220,32 +241,14 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         const size_t nb_image_expressions =
           (image_expression_node.is_type<language::expression_list>()) ? image_expression_node.children.size() : 1;
 
-        if (nb_image_domains != nb_image_expressions) {
-          if (image_domain_node.is_type<language::vector_type>()) {
-            ASTNodeDataType image_type = getVectorDataType(image_domain_node);
-            if (image_type.dimension() != nb_image_expressions) {
-              std::ostringstream message;
-              message << "expecting " << image_type.dimension() << " scalar expressions or an "
-                      << dataTypeName(image_type) << ", found " << nb_image_expressions << " scalar expressions";
-              throw ParseError(message.str(), image_expression_node.begin());
-            }
-          } else if (image_domain_node.is_type<language::matrix_type>()) {
-            ASTNodeDataType image_type = getMatrixDataType(image_domain_node);
-            if (image_type.numberOfRows() * image_type.numberOfColumns() != nb_image_expressions) {
-              std::ostringstream message;
-              message << "expecting " << image_type.numberOfRows() * image_type.numberOfColumns()
-                      << " scalar expressions or an " << dataTypeName(image_type) << ", found " << nb_image_expressions
-                      << " scalar expressions";
-              throw ParseError(message.str(), image_expression_node.begin());
-            }
-          } else {
-            std::ostringstream message;
-            message << "number of image spaces (" << nb_image_domains << ") " << rang::fgB::yellow
-                    << image_domain_node.string() << rang::style::reset << rang::style::bold
-                    << " differs from number of expressions (" << nb_image_expressions << ") " << rang::fgB::yellow
-                    << image_expression_node.string() << rang::style::reset;
-            throw ParseError(message.str(), image_domain_node.begin());
-          }
+        if ((not image_domain_node.is_type<language::tuple_type_specifier>()) and
+            (nb_image_domains != nb_image_expressions)) {
+          std::ostringstream message;
+          message << "number of image spaces (" << nb_image_domains << ") " << rang::fgB::yellow
+                  << image_domain_node.string() << rang::style::reset << rang::style::bold
+                  << " differs from number of expressions (" << nb_image_expressions << ") " << rang::fgB::yellow
+                  << image_expression_node.string() << rang::style::reset;
+          throw ParseError(message.str(), image_domain_node.begin());
         }
 
         this->_buildNodeDataTypes(image_expression_node);
@@ -520,6 +523,8 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         n.m_data_type = [&] {
           if (image_domain_node.m_data_type == ASTNodeDataType::type_id_t) {
             return ASTNodeDataType::build<ASTNodeDataType::type_id_t>(image_domain_node.m_data_type.nameOfTypeId());
+          } else if (image_domain_node.m_data_type == ASTNodeDataType::tuple_t) {
+            return ASTNodeDataType::build<ASTNodeDataType::tuple_t>(image_domain_node.m_data_type.contentType());
           } else {
             return image_domain_node.m_data_type.contentType();
           }
diff --git a/src/language/ast/ASTNodeDataTypeFlattener.cpp b/src/language/ast/ASTNodeDataTypeFlattener.cpp
index 00b77e0bf98e13314fce52396c7a6cef5717254e..201906e28020345cd70a34c501bcb2cf0b5fa258 100644
--- a/src/language/ast/ASTNodeDataTypeFlattener.cpp
+++ b/src/language/ast/ASTNodeDataTypeFlattener.cpp
@@ -39,6 +39,11 @@ ASTNodeDataTypeFlattener::ASTNodeDataTypeFlattener(ASTNode& node, FlattenedDataT
             flattened_datatype_list.push_back(
               {ASTNodeDataType::build<ASTNodeDataType::type_id_t>(image_sub_domain->m_data_type.nameOfTypeId()), node});
             break;
+          }
+          case ASTNodeDataType::tuple_t: {
+            flattened_datatype_list.push_back(
+              {ASTNodeDataType::build<ASTNodeDataType::tuple_t>(image_sub_domain->m_data_type.contentType()), node});
+            break;
           }
             // LCOV_EXCL_START
           default: {
diff --git a/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp b/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
index 656000c69cd45b85bcf72907850230c7e0f9615b..46ec44e1509ce607670b527d711284954a6670ac 100644
--- a/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
@@ -404,7 +404,7 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
         return std::make_unique<FunctionExpressionProcessor<ReturnT, ReturnT>>(function_component_expression);
       } else {
         // LCOV_EXCL_START
-        throw ParseError("unexpected error: invalid dimension for returned vector",
+        throw ParseError("unexpected error: invalid dimensions for returned matrix",
                          std::vector{function_component_expression.begin()});
         // LCOV_EXCL_STOP
       }
@@ -451,6 +451,547 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
     }
   };
 
+  auto get_function_processor_for_expression_tuple_of_value =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    using TupleContentT = std::decay_t<decltype(tuple_content_v)>;
+    switch (function_component_expression.m_data_type) {
+    case ASTNodeDataType::bool_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, bool>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::unsigned_int_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, uint64_t>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::int_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, int64_t>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::double_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, double>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::string_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::string>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::type_id_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, EmbeddedData>>(
+        function_component_expression);
+    }
+
+    case ASTNodeDataType::vector_t: {
+      switch (function_component_expression.m_data_type.dimension()) {
+      case 1: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyVector<1>>>(
+          function_component_expression);
+      }
+      case 2: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyVector<2>>>(
+          function_component_expression);
+      }
+      case 3: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyVector<3>>>(
+          function_component_expression);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::matrix_t: {
+      if (function_component_expression.m_data_type.numberOfRows() !=
+          function_component_expression.m_data_type.numberOfColumns()) {
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: invalid dimensions for returned matrix",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+      switch (function_component_expression.m_data_type.numberOfRows()) {
+      case 1: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyMatrix<1>>>(
+          function_component_expression);
+      }
+      case 2: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyMatrix<2>>>(
+          function_component_expression);
+      }
+      case 3: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyMatrix<3>>>(
+          function_component_expression);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::tuple_t: {
+      const ASTNodeDataType& tuple_content_type = function_component_expression.m_data_type.contentType();
+      switch (tuple_content_type) {
+      case ASTNodeDataType::bool_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<bool>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<uint64_t>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<int64_t>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::double_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<double>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::type_id_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<EmbeddedData>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::vector_t: {
+        switch (tuple_content_type.dimension()) {
+        case 1: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyVector<1>>>>(
+            function_component_expression);
+        }
+        case 2: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyVector<2>>>>(
+            function_component_expression);
+        }
+        case 3: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyVector<3>>>>(
+            function_component_expression);
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::matrix_t: {
+        if (tuple_content_type.numberOfRows() != tuple_content_type.numberOfColumns()) {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid dimensions for returned matrix",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+        switch (tuple_content_type.numberOfRows()) {
+        case 1: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyMatrix<1>>>>(
+            function_component_expression);
+        }
+        case 2: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyMatrix<2>>>>(
+            function_component_expression);
+        }
+        case 3: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyMatrix<3>>>>(
+            function_component_expression);
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::string_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<std::string>>>(
+          function_component_expression);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::list_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+        function_component_expression);
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw ParseError("unexpected error: undefined expression value type for function",
+                       std::vector{function_component_expression.begin()});
+    }
+      // LCOV_EXCL_STOP
+    }
+  };
+
+  auto get_function_processor_for_expression_tuple_of_vector =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    using TupleContentT = std::decay_t<decltype(tuple_content_v)>;
+    if constexpr (TupleContentT::Dimension == 1) {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::bool_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, bool>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, uint64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, int64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::double_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, double>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::vector_t: {
+        if (function_component_expression.m_data_type.dimension() == TupleContentT::Dimension) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid vector_t dimension",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::bool_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<bool>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::unsigned_int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<uint64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<int64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::double_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<double>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::vector_t: {
+          if (function_component_expression.m_data_type.contentType().dimension() == TupleContentT::Dimension) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid vector_t dimension",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::int_t: {
+        if (function_component_expression.is_type<language::integer>()) {
+          if (std::stoi(function_component_expression.string()) == 0) {
+            return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, ZeroType>>(
+              function_component_expression);
+          }
+        }
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::vector_t: {
+        if (function_component_expression.m_data_type.dimension() == TupleContentT::Dimension) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid vector_t dimension",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::vector_t: {
+          if (function_component_expression.m_data_type.contentType().dimension() == TupleContentT::Dimension) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid vector_t dimension",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  auto get_function_processor_for_expression_tuple_of_matrix =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    using TupleContentT = std::decay_t<decltype(tuple_content_v)>;
+    static_assert(TupleContentT::NumberOfColumns == TupleContentT::NumberOfRows);
+
+    if constexpr (TupleContentT::NumberOfRows == 1) {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::bool_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, bool>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, uint64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, int64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::double_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, double>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((function_component_expression.m_data_type.numberOfRows() == TupleContentT::NumberOfRows) and
+            (function_component_expression.m_data_type.numberOfColumns() == TupleContentT::NumberOfColumns)) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid matrix_t dimensions",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::bool_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<bool>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::unsigned_int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<uint64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<int64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::double_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<double>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::matrix_t: {
+          if ((function_component_expression.m_data_type.contentType().numberOfRows() ==
+               TupleContentT::NumberOfRows) and
+              (function_component_expression.m_data_type.contentType().numberOfColumns() ==
+               TupleContentT::NumberOfColumns)) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid matrix_t dimensions",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::int_t: {
+        if (function_component_expression.is_type<language::integer>()) {
+          if (std::stoi(function_component_expression.string()) == 0) {
+            return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, ZeroType>>(
+              function_component_expression);
+          }
+        }
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((function_component_expression.m_data_type.numberOfColumns() == TupleContentT::NumberOfColumns) and
+            (function_component_expression.m_data_type.numberOfRows() == TupleContentT::NumberOfRows)) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid matrix_t dimensions",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::matrix_t: {
+          if ((function_component_expression.m_data_type.contentType().numberOfColumns() ==
+               TupleContentT::NumberOfColumns) and
+              (function_component_expression.m_data_type.contentType().numberOfRows() == TupleContentT::NumberOfRows)) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid matrix_t dimensions",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  auto get_function_processor_for_expression_tuple =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    switch (tuple_content_v) {
+    case ASTNodeDataType::vector_t: {
+      switch (tuple_content_v.dimension()) {
+      case 1: {
+        return get_function_processor_for_expression_tuple_of_vector(TinyVector<1>{});
+      }
+      case 2: {
+        return get_function_processor_for_expression_tuple_of_vector(TinyVector<2>{});
+      }
+      case 3: {
+        return get_function_processor_for_expression_tuple_of_vector(TinyVector<3>{});
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: invalid vector_t dimension",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::matrix_t: {
+      if (tuple_content_v.numberOfRows() == tuple_content_v.numberOfColumns()) {
+        switch (tuple_content_v.numberOfRows()) {
+        case 1: {
+          return get_function_processor_for_expression_tuple_of_matrix(TinyMatrix<1>{});
+        }
+        case 2: {
+          return get_function_processor_for_expression_tuple_of_matrix(TinyMatrix<2>{});
+        }
+        case 3: {
+          return get_function_processor_for_expression_tuple_of_matrix(TinyMatrix<3>{});
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: invalid vector_t dimension",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: invalid dimensions for returned matrix",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::bool_t: {
+      return get_function_processor_for_expression_tuple_of_value(bool{});
+    }
+    case ASTNodeDataType::unsigned_int_t: {
+      return get_function_processor_for_expression_tuple_of_value(uint64_t{});
+    }
+    case ASTNodeDataType::int_t: {
+      return get_function_processor_for_expression_tuple_of_value(int64_t{});
+    }
+    case ASTNodeDataType::double_t: {
+      return get_function_processor_for_expression_tuple_of_value(double{});
+    }
+    case ASTNodeDataType::type_id_t: {
+      return get_function_processor_for_expression_tuple_of_value(EmbeddedData{});
+    }
+    case ASTNodeDataType::string_t: {
+      return get_function_processor_for_expression_tuple_of_value(std::string{});
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid tuple content type");
+    }
+      // LCOV_EXCL_STOP
+    }
+  };
+
   auto get_function_processor_for_value = [&]() {
     switch (return_value_type) {
     case ASTNodeDataType::bool_t: {
@@ -516,6 +1057,9 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
     }
     case ASTNodeDataType::type_id_t: {
       return get_function_processor_for_expression_type_id(return_value_type.nameOfTypeId());
+    }
+    case ASTNodeDataType::tuple_t: {
+      return get_function_processor_for_expression_tuple(return_value_type.contentType());
     }
       // LCOV_EXCL_START
     default: {
@@ -548,6 +1092,9 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
       }
       case ASTNodeDataType::type_id_t: {
         return ASTNodeDataType::build<ASTNodeDataType::type_id_t>(image_domain_node.m_data_type.nameOfTypeId());
+      }
+      case ASTNodeDataType::tuple_t: {
+        return ASTNodeDataType::build<ASTNodeDataType::tuple_t>(image_domain_node.m_data_type.contentType());
       }
         // LCOV_EXCL_START
       default: {
@@ -566,22 +1113,6 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
   ASTNode& function_image_domain = *function_descriptor.domainMappingNode().children[1];
   ASTNode& function_expression   = *function_descriptor.definitionNode().children[1];
 
-  const ASTNodeDataType function_return_type = [&] {
-    switch (function_image_domain.m_data_type) {
-    case ASTNodeDataType::typename_t: {
-      return function_image_domain.m_data_type.contentType();
-    }
-    case ASTNodeDataType::type_id_t: {
-      return ASTNodeDataType::build<ASTNodeDataType::type_id_t>(function_image_domain.m_data_type.nameOfTypeId());
-    }
-      // LCOV_EXCL_START
-    default: {
-      throw UnexpectedError("invalid function return type");
-    }
-      // LCOV_EXCL_STOP
-    }
-  }();
-
   if (function_image_domain.is_type<language::vector_type>()) {
     ASTNodeDataType vector_type = getVectorDataType(function_image_domain);
 
@@ -664,6 +1195,10 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
       node.m_node_processor = std::move(function_processor);
     }
 
+  } else if (function_image_domain.is_type<language::tuple_type_specifier>()) {
+    add_component_expression(function_expression, function_image_domain);
+
+    node.m_node_processor = std::move(function_processor);
   } else {
     if (function_expression.is_type<language::expression_list>()) {
       ASTNode& image_domain_node = function_image_domain;
diff --git a/src/language/node_processor/FunctionArgumentConverter.hpp b/src/language/node_processor/FunctionArgumentConverter.hpp
index 6a4075307e3af9f225cd493176fbd0a7873a3c75..36c5bab29c2517cffbec47476f2d74da55943667 100644
--- a/src/language/node_processor/FunctionArgumentConverter.hpp
+++ b/src/language/node_processor/FunctionArgumentConverter.hpp
@@ -131,8 +131,10 @@ class FunctionTinyVectorArgumentConverter final : public IFunctionArgumentConver
           std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value)));
       }
     } else {
+      // LCOV_EXCL_START
       throw UnexpectedError(std::string{"cannot convert '"} + demangle<ProvidedValueType>() + "' to '" +
                             demangle<ExpectedValueType>() + "'");
+      // LCOV_EXCL_STOP
     }
     return {};
   }
@@ -193,8 +195,10 @@ class FunctionTinyMatrixArgumentConverter final : public IFunctionArgumentConver
           std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value)));
       }
     } else {
+      // LCOV_EXCL_START
       throw UnexpectedError(std::string{"cannot convert '"} + demangle<ProvidedValueType>() + "' to '" +
                             demangle<ExpectedValueType>() + "'");
+      // LCOV_EXCL_STOP
     }
 
     return {};
diff --git a/src/language/node_processor/FunctionProcessor.hpp b/src/language/node_processor/FunctionProcessor.hpp
index bc8c3c0bdd6d9cd5a7e1b8fb91f7e21e3c45f8a8..2b3e65ecb130c6f50105a5cba08552e43e16596c 100644
--- a/src/language/node_processor/FunctionProcessor.hpp
+++ b/src/language/node_processor/FunctionProcessor.hpp
@@ -38,6 +38,224 @@ class FunctionExpressionProcessor final : public INodeProcessor
                          (is_tiny_vector_v<ReturnType> or is_tiny_matrix_v<ReturnType>)) {
       static_assert(ReturnType::Dimension == 1, "invalid conversion");
       return ReturnType(std::get<ExpressionValueType>(m_function_expression.execute(exec_policy)));
+    } else if constexpr (is_std_vector_v<ReturnType>) {
+      auto expression_value = m_function_expression.execute(exec_policy);
+
+      const ExpressionValueType& v = std::get<ExpressionValueType>(expression_value);
+
+      using TupleContentType = std::decay_t<typename ReturnType::value_type>;
+
+      if constexpr (std::is_convertible_v<ExpressionValueType, TupleContentType>) {
+        if constexpr (std::is_same_v<TupleContentType, uint64_t> and std::is_same_v<ExpressionValueType, int64_t>) {
+          if (v < 0) {
+            throw std::domain_error("trying to convert negative value (" + stringify(v) + ")");
+          }
+        }
+        return ReturnType{static_cast<TupleContentType>(v)};
+      } else if constexpr (is_tiny_matrix_v<TupleContentType>) {
+        static_assert(ReturnType::value_type::NumberOfRows == ReturnType::value_type::NumberOfColumns);
+        if constexpr (ReturnType::value_type::NumberOfRows == 1) {
+          if constexpr (std::is_arithmetic_v<ExpressionValueType>) {
+            return ReturnType{TupleContentType(v)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr ((std::is_same_v<ListElementType, TupleContentType>) or
+                                ((std::is_arithmetic_v<ListElementType>))) {
+                    result[i] = static_cast<TupleContentType>(v_i);
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else {
+            static_assert(is_std_vector_v<ExpressionValueType>);
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              result[i] = static_cast<TupleContentType>(v[i]);
+            }
+            return result;
+          }
+        } else {
+          if constexpr (std::is_same_v<ExpressionValueType, int64_t>) {
+            return ReturnType{TupleContentType(ZeroType::zero)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr (std::is_same_v<ListElementType, TupleContentType>) {
+                    result[i] = v_i;
+                  } else if constexpr (std::is_same_v<ListElementType, int64_t>) {
+                    result[i] = ZeroType::zero;
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else if constexpr (is_std_vector_v<ExpressionValueType>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              if constexpr (std::is_same_v<typename ExpressionValueType::value_type, TupleContentType>) {
+                result[i] = v[i];
+              } else if constexpr (std::is_same_v<typename ExpressionValueType::value_type, int64_t>) {
+                result[i] = ZeroType::zero;
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("invalid list element type");
+                // LCOV_EXCL_STOP
+              }
+            }
+            return result;
+          } else {
+            // LCOV_EXCL_START
+            throw UnexpectedError("invalid expression value");
+            // LCOV_EXCL_STOP
+          }
+        }
+      } else if constexpr (is_tiny_vector_v<TupleContentType>) {
+        if constexpr (ReturnType::value_type::Dimension == 1) {
+          if constexpr (std::is_arithmetic_v<ExpressionValueType>) {
+            return ReturnType{TupleContentType(v)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr ((std::is_same_v<ListElementType, TupleContentType>) or
+                                ((std::is_arithmetic_v<ListElementType>))) {
+                    result[i] = static_cast<TupleContentType>(v_i);
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else {
+            static_assert(is_std_vector_v<ExpressionValueType>);
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              result[i] = static_cast<TupleContentType>(v[i]);
+            }
+            return result;
+          }
+        } else {
+          if constexpr (std::is_same_v<ExpressionValueType, int64_t>) {
+            return ReturnType{TupleContentType(ZeroType::zero)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr (std::is_same_v<ListElementType, TupleContentType>) {
+                    result[i] = v_i;
+                  } else if constexpr (std::is_same_v<ListElementType, int64_t>) {
+                    result[i] = ZeroType::zero;
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else if constexpr (is_std_vector_v<ExpressionValueType>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              if constexpr (std::is_same_v<typename ExpressionValueType::value_type, TupleContentType>) {
+                result[i] = v[i];
+              } else if constexpr (std::is_same_v<typename ExpressionValueType::value_type, int64_t>) {
+                result[i] = ZeroType::zero;
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("invalid tuple element type");
+                // LCOV_EXCL_STOP
+              }
+            }
+            return result;
+          }
+        }
+        // LCOV_EXCL_START
+        throw UnexpectedError("invalid expression type");
+        // LCOV_EXCL_STOP
+      } else if constexpr (std::is_same_v<TupleContentType, std::string>) {
+        return ReturnType{stringify(v)};
+      } else if constexpr (is_std_vector_v<ExpressionValueType>) {
+        using ExpressionContentType = typename ExpressionValueType::value_type;
+        if constexpr (std::is_convertible_v<ExpressionContentType, TupleContentType>) {
+          ReturnType result(v.size());
+          for (size_t i = 0; i < v.size(); ++i) {
+            if constexpr (std::is_same_v<TupleContentType, uint64_t> and
+                          std::is_same_v<ExpressionContentType, int64_t>) {
+              if (v[i] < 0) {
+                throw std::domain_error("trying to convert negative value (" + stringify(v[i]) + ")");
+              }
+            }
+            result[i] = v[i];
+          }
+          return result;
+        } else if constexpr (std::is_same_v<TupleContentType, std::string>) {
+          ReturnType result(v.size());
+          for (size_t i = 0; i < v.size(); ++i) {
+            result[i] = stringify(v[i]);
+          }
+          return result;
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid tuple type");
+          // LCOV_EXCL_STOP
+        }
+      } else if constexpr (std::is_same_v<AggregateDataVariant, ExpressionValueType>) {
+        ReturnType result(v.size());
+        for (size_t i = 0; i < result.size(); ++i) {
+          std::visit(
+            [&](auto&& v_i) {
+              using ListElementType = std::decay_t<decltype(v_i)>;
+              if constexpr (std::is_convertible_v<ListElementType, TupleContentType>) {
+                if constexpr (std::is_same_v<TupleContentType, uint64_t> and std::is_same_v<ListElementType, int64_t>) {
+                  if (v_i < 0) {
+                    throw std::domain_error("trying to convert negative value (" + stringify(v_i) + ")");
+                  }
+                }
+                result[i] = static_cast<TupleContentType>(v_i);
+              } else if constexpr (std::is_same_v<TupleContentType, std::string>) {
+                if constexpr (std::is_same_v<ListElementType, std::string>) {
+                  result[i] = std::move(v_i);
+                } else {
+                  result[i] = stringify(v_i);
+                }
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("invalid list element type");
+                // LCOV_EXCL_STOP
+              }
+            },
+            v[i]);
+        }
+        return result;
+      } else {
+        // LCOV_EXCL_START
+        throw UnexpectedError("invalid tuple type");
+        // LCOV_EXCL_STOP
+      }
     } else {
       // LCOV_EXCL_START
       throw UnexpectedError("invalid conversion");
diff --git a/tests/test_ASTNodeDataTypeBuilder.cpp b/tests/test_ASTNodeDataTypeBuilder.cpp
index 5c449d7abf9bc9f083bd7c8206d377360f0f229c..b6dc2cac85ab783eba86ba4b57a59a38edd6aac2 100644
--- a/tests/test_ASTNodeDataTypeBuilder.cpp
+++ b/tests/test_ASTNodeDataTypeBuilder.cpp
@@ -414,7 +414,7 @@ let square : R -> R^3, x -> (x, 2);
         ASTSymbolTableBuilder{*ast};
 
         REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast},
-                            "expecting 3 scalar expressions or an R^3, found 2 scalar expressions");
+                            "number of image spaces (1) R^3 differs from number of expressions (2) (x, 2)");
       }
     }
   }
@@ -1126,7 +1126,7 @@ let f : R -> R^2x2, x -> (x, 2*x, 2);
         ASTSymbolTableBuilder{*ast};
 
         REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast},
-                            "expecting 4 scalar expressions or an R^2x2, found 3 scalar expressions");
+                            "number of image spaces (1) R^2x2 differs from number of expressions (3) (x, 2*x, 2)");
       }
 
       SECTION("undefined type identifier")
diff --git a/tests/test_ASTNodeFunctionExpressionBuilder.cpp b/tests/test_ASTNodeFunctionExpressionBuilder.cpp
index 762202a671b1f7708b193ca8cd6c7aa599bfc780..1d3ef390960545ace5950c5c48f092f85c420b03 100644
--- a/tests/test_ASTNodeFunctionExpressionBuilder.cpp
+++ b/tests/test_ASTNodeFunctionExpressionBuilder.cpp
@@ -115,107 +115,111 @@ const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const dou
 
 TEST_CASE("ASTNodeFunctionExpressionBuilder", "[language]")
 {
-  SECTION("return a B")
+  SECTION("value or compounds of values")
   {
-    SECTION("B argument")
+    SECTION("without conversion")
     {
-      SECTION("B parameter")
+      SECTION("return a B")
       {
-        std::string_view data = R"(
+        SECTION("B argument")
+        {
+          SECTION("B parameter")
+          {
+            std::string_view data = R"(
 let not_v : B -> B, a -> not a;
 not_v(true);
 )";
 
-        std::string_view result = R"(
+            std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:not_v:NameProcessor)
      `-(language::true_kw:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
-      }
-    }
+            CHECK_AST(data, result);
+          }
+        }
 
-    SECTION("N argument")
-    {
-      std::string_view data = R"(
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
 let test : N -> B, n -> n<10;
 test(2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
 let test : Z -> B, z -> z>3;
 test(2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R argument")
-    {
-      std::string_view data = R"(
+        SECTION("R argument")
+        {
+          std::string_view data = R"(
 let test : R -> B, x -> x>2.3;
 test(2.1);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::real:2.1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
+      }
 
-  SECTION("return a N")
-  {
-    SECTION("N argument")
-    {
-      std::string_view data = R"(
+      SECTION("return a N")
+      {
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
 let test : N -> N, n -> n+2;
 test(2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
 let absolute : Z -> N, z -> (z>0)*z -(z<=0)*z;
 absolute(-2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:absolute:NameProcessor)
@@ -223,37 +227,37 @@ absolute(-2);
          `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
+      }
 
-  SECTION("return a Z")
-  {
-    SECTION("N argument")
-    {
-      std::string_view data = R"(
+      SECTION("return a Z")
+      {
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
 let minus : N -> Z, n -> -n;
 minus(true);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:minus:NameProcessor)
      `-(language::true_kw:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
 let times_2 : Z -> Z, z -> z*2;
 times_2(-2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:times_2:NameProcessor)
@@ -261,20 +265,20 @@ times_2(-2);
          `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
+      }
 
-  SECTION("return a string")
-  {
-    SECTION("string argument")
-    {
-      std::string_view data = R"(
+      SECTION("return a string")
+      {
+        SECTION("string argument")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", "bar");
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -283,17 +287,17 @@ cat("foo", "bar");
          `-(language::literal:"bar":ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("B argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("B argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", true);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -302,18 +306,18 @@ cat("foo", true);
          `-(language::true_kw:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("N argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("N argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 let n : N, n = 2;
 cat("foo", n);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -322,17 +326,17 @@ cat("foo", n);
          `-(language::name:n:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", -1);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -342,17 +346,17 @@ cat("foo", -1);
              `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("R argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", 2.5e-3);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -361,12 +365,13 @@ cat("foo", 2.5e-3);
          `-(language::real:2.5e-3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
+      }
 
-    SECTION("Return R^1 -> R^1")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^1")
+      {
+        std::string_view data = R"(
 let f : R^1 -> R^1, x -> x+x;
 let x : R^1, x = 1;
 f(x);
@@ -377,7 +382,7 @@ f(2);
 f(1.4);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  +-(language::function_evaluation:FunctionProcessor)
  |   +-(language::name:f:NameProcessor)
@@ -396,48 +401,48 @@ f(1.4);
      `-(language::real:1.4:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^2 -> R^2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2")
+      {
+        std::string_view data = R"(
 let f : R^2 -> R^2, x -> x+x;
 let x : R^2, x = (1,2);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^3 -> R^3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3")
+      {
+        std::string_view data = R"(
 let f : R^3 -> R^3, x -> x+x;
 let x : R^3, x = (1,2,3);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^1x1 -> R^1x1")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^1x1")
+      {
+        std::string_view data = R"(
 let f : R^1x1 -> R^1x1, x -> x+x;
 let x : R^1x1, x = 1;
 f(x);
@@ -448,7 +453,7 @@ f(2);
 f(1.4);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  +-(language::function_evaluation:FunctionProcessor)
  |   +-(language::name:f:NameProcessor)
@@ -467,70 +472,70 @@ f(1.4);
      `-(language::real:1.4:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^2x2 -> R^2x2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2x2")
+      {
+        std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x+x;
 let x : R^2x2, x = (1,2,3,4);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^3x3 -> R^3x3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3x3")
+      {
+        std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x+x;
 let x : R^3x3, x = (1,2,3,4,5,6,7,8,9);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return scalar -> R^1")
-    {
-      std::string_view data = R"(
-let f : R -> R^1, x -> x+1;
+      SECTION("Return a R^1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1, x -> [x+1];
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2")
+      {
+        std::string_view data = R"(
 let f : R*R -> R^2, (x,y) -> [x,y];
 f(1,2);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -539,17 +544,17 @@ f(1,2);
          `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3")
+      {
+        std::string_view data = R"(
 let f : R*R*R -> R^3, (x,y,z) -> [x,y,z];
 f(1,2,3);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -559,34 +564,34 @@ f(1,2,3);
          `-(language::integer:3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return scalar -> R^1x1")
-    {
-      std::string_view data = R"(
-let f : R -> R^1x1, x -> x+1;
+      SECTION("Return a R^1x1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1x1, x -> [[x+1]];
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^2x2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2x2")
+      {
+        std::string_view data = R"(
 let f : R*R*R*R -> R^2x2, (x,y,z,t) -> [[x,y],[z,t]];
 f(1,2,3,4);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -597,17 +602,17 @@ f(1,2,3,4);
          `-(language::integer:4:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^3x3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3x3")
+      {
+        std::string_view data = R"(
 let f : R^3*R^3*R^3 -> R^3x3, (x,y,z) -> [[x[0],x[1],x[2]],[y[0],y[1],y[2]],[z[0],z[1],z[2]]];
 f([1,2,3],[4,5,6],[7,8,9]);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -626,298 +631,360 @@ f([1,2,3],[4,5,6],[7,8,9]);
              `-(language::integer:9:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^1")
-    {
-      std::string_view data = R"(
-let f : R -> R^1, x -> 0;
-f(1);
+      SECTION("return a builtin_t")
+      {
+        SECTION("builtin_t argument")
+        {
+          std::string_view data = R"(
+let foo : builtin_t -> builtin_t, b -> b;
+let b0 : builtin_t;
+foo(b0);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<1ul, double>, ZeroType>)
-     +-(language::name:f:NameProcessor)
-     `-(language::integer:1:ValueProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
+      }
     }
 
-    SECTION("Return '0' -> R^2")
+    SECTION("with return conversion")
     {
-      std::string_view data = R"(
+      SECTION("Return scalar -> R^1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1, x -> x+1;
+f(1);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return scalar -> R^1x1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1x1, x -> x+1;
+f(1);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return '0' -> R^1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1, x -> 0;
+f(1);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<1ul, double>, ZeroType>)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return '0' -> R^2")
+      {
+        std::string_view data = R"(
 let f : R -> R^2, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<2ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^3")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^3")
+      {
+        std::string_view data = R"(
 let f : R -> R^3, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<3ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^1x1")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^1x1")
+      {
+        std::string_view data = R"(
 let f : R -> R^1x1, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyMatrix<1ul, 1ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^2x2")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^2x2")
+      {
+        std::string_view data = R"(
 let f : R -> R^2x2, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyMatrix<2ul, 2ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^3x3")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^3x3")
+      {
+        std::string_view data = R"(
 let f : R -> R^3x3, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyMatrix<3ul, 3ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^d compound")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, [x], [x,y], [x,y,z]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^1")
+      {
+        std::string_view data = R"(
+let f : R^1 -> R^1, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^dxd compound")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, [[x]], [[x,y],[z,t]], [[x,y,z], [x,x,x], [t,t,t]]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^2")
+      {
+        std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^d compound with '0'")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, 0, 0, [x,y,z]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^3")
+      {
+        std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^dxd compound with '0'")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, 0, 0, [[x, y, z], [t, x, y], [z, t, x]]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^1x1")
+      {
+        std::string_view data = R"(
+let f : R^1x1 -> R^1x1, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Arguments '0' -> R^1")
-    {
-      std::string_view data = R"(
-let f : R^1 -> R^1, x -> x;
+      SECTION("Arguments '0' -> R^2x2")
+      {
+        std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
 f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Arguments '0' -> R^2")
-    {
-      std::string_view data = R"(
-let f : R^2 -> R^2, x -> x;
+      SECTION("Arguments '0' -> R^3x3")
+      {
+        std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
 f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
+        CHECK_AST(data, result);
+      }
     }
+  }
 
-    SECTION("Arguments '0' -> R^3")
+  SECTION("compound return type")
+  {
+    SECTION("Return compound with R^d")
     {
       std::string_view data = R"(
-let f : R^3 -> R^3, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, [x], [x,y], [x,y,z]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments '0' -> R^1x1")
+    SECTION("Return compound with R^dxd")
     {
       std::string_view data = R"(
-let f : R^1x1 -> R^1x1, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, [[x]], [[x,y],[z,t]], [[x,y,z], [x,x,x], [t,t,t]]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments '0' -> R^2x2")
+    SECTION("Return R^d compound with '0'")
     {
       std::string_view data = R"(
-let f : R^2x2 -> R^2x2, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, 0, 0, [x,y,z]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments '0' -> R^3x3")
+    SECTION("Return R^dxd compound with '0'")
     {
       std::string_view data = R"(
-let f : R^3x3 -> R^3x3, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, 0, 0, [[x, y, z], [t, x, y], [z, t, x]]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments tuple -> R^d")
+    SECTION("Arguments R^d")
     {
       std::string_view data = R"(
 let f: R^3 -> R, x -> x[0]+x[1]+x[2];
@@ -966,7 +1033,7 @@ f([[1,2,3],[4,5,6],[7,8,9]]);
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments compound with tuple")
+    SECTION("Arguments compound with R^d and R^dxd")
     {
       std::string_view data = R"(
 let f: R*R^3*R^2x2->R, (t,x,y) -> t*(x[0]+x[1]+x[2])*y[0,0]+y[1,1];
@@ -995,478 +1062,2892 @@ f(2,[1,2,3],[[2,3],[-1,1.3]]);
 
       CHECK_AST(data, result);
     }
-  }
 
-  SECTION("return a builtin_t")
-  {
-    SECTION("builtin_t argument")
+    SECTION("errors")
     {
-      std::string_view data = R"(
-let foo : builtin_t -> builtin_t, b -> b;
-let b0 : builtin_t;
-foo(b0);
+      SECTION("wrong argument number")
+      {
+        std::string_view data = R"(
+let Id : Z -> Z, z -> z;
+Id(2,3);
 )";
 
-      std::string_view result = R"(
-(root:ASTNodeListProcessor)
- `-(language::function_evaluation:FunctionProcessor)
-     +-(language::name:foo:NameProcessor)
-     `-(language::name:b0:NameProcessor)
-)";
-
-      CHECK_AST(data, result);
-    }
-  }
-
-  SECTION("errors")
-  {
-    SECTION("wrong argument number")
-    {
-      std::string_view data = R"(
-let Id : Z -> Z, z -> z;
-Id(2,3);
-)";
-
-      CHECK_AST_THROWS(data);
-    }
+        CHECK_AST_THROWS(data);
+      }
 
-    SECTION("wrong argument number 2")
-    {
-      std::string_view data = R"(
+      SECTION("wrong argument number 2")
+      {
+        std::string_view data = R"(
 let sum : R*R -> R, (x,y) -> x+y;
 sum(2);
 )";
 
-      CHECK_AST_THROWS(data);
-    }
+        CHECK_AST_THROWS(data);
+      }
 
-    SECTION("invalid return implicit conversion")
-    {
-      SECTION("string -> R")
+      SECTION("invalid return implicit conversion")
       {
-        std::string_view data = R"(
+        SECTION("string -> R")
+        {
+          std::string_view data = R"(
 let bad_conv : string -> R, s -> s;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
 
-      SECTION("R -> B")
-      {
-        std::string_view data = R"(
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
 let bad_B : R -> B, x -> x;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
 
-      SECTION("R -> N")
-      {
-        std::string_view data = R"(
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
 let next : R -> N, x -> x;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
 
-      SECTION("R -> Z")
-      {
-        std::string_view data = R"(
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
 let prev : R -> Z, x -> x;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
 
-      SECTION("N -> B")
-      {
-        std::string_view data = R"(
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
 let bad_B : N -> B, n -> n;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
 
-      SECTION("Z -> B")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
 let bad_B : Z -> B, n -> n;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
       }
-    }
 
-    SECTION("invalid argument implicit conversion")
-    {
-      SECTION("N -> B")
+      SECTION("invalid argument implicit conversion")
       {
-        std::string_view data = R"(
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
 let negate : B -> B, b -> not b;
 let n : N, n = 2;
 negate(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
 
-      SECTION("Z -> B")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
 let negate : B -> B, b -> not b;
 negate(3-4);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
 
-      SECTION("R -> B")
-      {
-        std::string_view data = R"(
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
 let negate : B -> B, b -> not b;
 negate(3.24);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
 
-      SECTION("R -> N")
-      {
-        std::string_view data = R"(
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
 let next : N -> N, n -> n+1;
 next(3.24);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
 
-      SECTION("R -> Z")
-      {
-        std::string_view data = R"(
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
 let prev : Z -> Z, z -> z-1;
 prev(3 + .24);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
 
-      SECTION("B -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
+        }
 
-      SECTION("N -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
+        }
 
-      SECTION("Z -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
 
-      SECTION("R -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
+        }
 
-      SECTION("B -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
+        }
 
-      SECTION("N -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
+        }
 
-      SECTION("Z -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+        }
 
-      SECTION("R -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
+        }
 
-      SECTION("B -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
+        }
 
-      SECTION("N -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
+        }
 
-      SECTION("Z -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
+        }
 
-      SECTION("R -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
+        }
 
-      SECTION("B -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
+        }
 
-      SECTION("N -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
+        }
 
-      SECTION("Z -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
+        }
 
-      SECTION("R -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+        }
       }
-    }
 
-    SECTION("arguments invalid tuple -> R^d conversion")
-    {
-      SECTION("tuple[2] -> R^2")
+      SECTION("arguments invalid tuple -> R^d conversion")
       {
-        std::string_view data = R"(
+        SECTION("tuple[2] -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R, x->x[0];
 f((1,2));
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
 
-      SECTION("tuple[3] -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("tuple[3] -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R, x->x[0];
 f((1,2,3));
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
 
-      SECTION("compound tuple[2] -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("compound -> R^2")
+        {
+          std::string_view data = R"(
 let f : R*R^2 -> R, (t,x)->x[0];
 f(1,(1,2));
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
 
-      SECTION("compound tuple[2] -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("compound -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3*R^2 -> R, (x,y)->x[0]*y[1];
 f((1,2,3),[3,4]);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
 
-      SECTION("list instead of tuple -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("list instead of tuple -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R, x -> x[0]*x[1];
 f(1,2,3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 1, provided 3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 1, provided 3"});
+        }
 
-      SECTION("list instead of tuple -> R^3*R^2")
-      {
-        std::string_view data = R"(
+        SECTION("list instead of tuple -> R^3*R^2")
+        {
+          std::string_view data = R"(
 let f : R^3*R^2 -> R, (x,y) -> x[0]*x[1]-y[0];
 f([1,2,3],2,3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 2, provided 3"});
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 2, provided 3"});
+        }
       }
-    }
 
-    SECTION("non pure function")
-    {
-      SECTION("argument modification")
+      SECTION("non pure function")
       {
-        SECTION("++ argument")
+        SECTION("argument modification")
         {
-          std::string_view data = R"(
+          SECTION("++ argument")
+          {
+            std::string_view data = R"(
 let non_pure : N -> N, x -> 3 * ++x;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("argument ++")
-        {
-          std::string_view data = R"(
+          SECTION("argument ++")
+          {
+            std::string_view data = R"(
 let non_pure : N -> N, x -> 1 + x ++;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("-- argument")
-        {
-          std::string_view data = R"(
+          SECTION("-- argument")
+          {
+            std::string_view data = R"(
 let non_pure : Z -> Z, x -> 3 * --x;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("argument --")
-        {
-          std::string_view data = R"(
+          SECTION("argument --")
+          {
+            std::string_view data = R"(
 let non_pure : Z -> Z, x -> 1 + x --;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
         }
-      }
 
-      SECTION("outer variable modification")
-      {
-        SECTION("++ outer variable")
+        SECTION("outer variable modification")
         {
-          std::string_view data = R"(
+          SECTION("++ outer variable")
+          {
+            std::string_view data = R"(
 let a:N, a = 4;
 let non_pure : Z -> Z, x -> x * ++a;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("outer variable ++")
-        {
-          std::string_view data = R"(
+          SECTION("outer variable ++")
+          {
+            std::string_view data = R"(
 let a:N, a = 4;
 let non_pure : N -> N, x -> x + a++;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("-- outer variable")
-        {
-          std::string_view data = R"(
+          SECTION("-- outer variable")
+          {
+            std::string_view data = R"(
 let a:Z, a = 4;
 let non_pure : Z -> Z, x -> x * --a;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("outer variable --")
+          {
+            std::string_view data = R"(
+let a:Z, a = 4;
+let non_pure : Z -> Z, x -> x + a--;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
         }
+      }
+    }
+  }
 
-        SECTION("outer variable --")
+  SECTION("tuples")
+  {
+    SECTION("from list of values")
+    {
+      SECTION("return a (B)")
+      {
+        SECTION("B argument")
         {
-          std::string_view data = R"(
+          SECTION("B parameter")
+          {
+            std::string_view data = R"(
+let not_v : B -> (B), a -> (not a, a, true);
+not_v(true);
+)";
+
+            std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:not_v:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+            CHECK_AST(data, result);
+          }
+        }
+
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let test : N -> (B), n -> (n<10, n>1, false);
+test(2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let test : Z -> (B), z -> (z>3, z<5);
+test(2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R argument")
+        {
+          std::string_view data = R"(
+let test : R -> (B), x -> (x>2.3, 2*x>3);
+test(2.1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::real:2.1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (N)")
+      {
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let test : N -> (N), n -> (n+2, n, true);
+test(2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let absolute : Z -> (N), z -> ((z>0)*z -(z<=0)*z, 3, true);
+absolute(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:absolute:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (Z)")
+      {
+        SECTION("B argument")
+        {
+          std::string_view data = R"(
+let minus : B -> (Z), b -> (-b, b, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let minus : N -> (Z), n -> (-n, n, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let times_2 : Z -> (Z), z -> (z*2, 2, false);
+times_2(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:times_2:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (R)")
+      {
+        SECTION("B argument")
+        {
+          std::string_view data = R"(
+let minus : B -> (Z), b -> (-b, b, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let minus : N -> (R), n -> (-n, n, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let times_2 : Z -> (R), z -> (z*2, 2, false);
+times_2(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:times_2:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R argument")
+        {
+          std::string_view data = R"(
+let times_2 : R -> (R), r -> (r*2, 2, false);
+times_2(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:times_2:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (string)")
+      {
+        SECTION("string argument")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, true, -3, 2.4);
+cat("foo", "bar");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::literal:"bar":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("B argument conversion")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, true, 2, 2.5);
+cat("foo", true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N argument conversion")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, 4, [1.2], [2,2]);
+let n : N, n = 2;
+cat("foo", n);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::name:n:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument conversion")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, s2);
+cat("foo", -1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+             `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R argument conversion")
+        {
+          std::string_view data   = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, 1.3);
+cat("foo", 2.5e-3);
+)";
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::real:2.5e-3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("Return a (R^1)")
+      {
+        std::string_view data = R"(
+let f : R^1 -> (R^1), x -> (x+x, 2.3, [1.2]);
+let x : R^1, x = 1;
+f(x);
+let n:N, n=1;
+f(true);
+f(n);
+f(2);
+f(1.4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:x:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:n:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::real:1.4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^2)")
+      {
+        std::string_view data = R"(
+let f : R^2 -> (R^2), x -> (x+x, 0, [1, 3]);
+let x : R^2, x = (1,2);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^3)")
+      {
+        std::string_view data = R"(
+let f : R^3 -> (R^3), x -> (x+x, 2*x, [1,2,3], 0);
+let x : R^3, x = (1,2,3);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^1x1)")
+      {
+        std::string_view data = R"(
+let f : R^1x1 -> (R^1x1), x -> (x+x, [[1]], 3, 0);
+let x : R^1x1, x = 1;
+f(x);
+let n:N, n=1;
+f(true);
+f(n);
+f(2);
+f(1.4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:x:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:n:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::real:1.4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^2x2)")
+      {
+        std::string_view data = R"(
+let f : R^2x2 -> (R^2x2), x -> (x+x, 0, [[1,2],[3,4]]);
+let x : R^2x2, x = (1,2,3,4);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^3x3)")
+      {
+        std::string_view data = R"(
+let f : R^3x3 -> (R^3x3), x -> (x+x, 0, [[1,2,3],[4,5,6],[7,8,9]]);
+let x : R^3x3, x = (1,2,3,4,5,6,7,8,9);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (builtin_t)")
+      {
+        std::string_view data = R"(
+let foo : builtin_t -> (builtin_t), b -> (b, b, b);
+let b0 : builtin_t;
+foo(b0);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+    }
+
+    SECTION("from single value")
+    {
+      SECTION("without conversion")
+      {
+        SECTION("Return a (B)")
+        {
+          std::string_view data = R"(
+let f : B -> (B), b -> b;
+f(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N)")
+        {
+          std::string_view data = R"(
+let f : N -> (N), n -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z)")
+        {
+          std::string_view data = R"(
+let f : Z -> (Z), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R)")
+        {
+          std::string_view data = R"(
+let f : R -> (R), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (string)")
+        {
+          std::string_view data = R"(
+let f : string -> (string), s -> s;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1), x -> [x+1];
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2)")
+        {
+          std::string_view data = R"(
+let f : R*R -> (R^2), (x,y) -> [x,y];
+f(1,2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3)")
+        {
+          std::string_view data = R"(
+let f : R*R*R -> (R^3), (x,y,z) -> [x,y,z];
+f(1,2,3);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         `-(language::integer:3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1x1), x -> [[x+1]];
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2x2)")
+        {
+          std::string_view data = R"(
+let f : R*R*R*R -> (R^2x2), (x,y,z,t) -> [[x,y],[z,t]];
+f(1,2,3,4);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3x3)")
+        {
+          std::string_view data = R"(
+let f : R^3*R^3*R^3 -> (R^3x3), (x,y,z) -> [[x[0],x[1],x[2]],[y[0],y[1],y[2]],[z[0],z[1],z[2]]];
+f([1,2,3],[4,5,6],[7,8,9]);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:1:ValueProcessor)
+         |   +-(language::integer:2:ValueProcessor)
+         |   `-(language::integer:3:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:4:ValueProcessor)
+         |   +-(language::integer:5:ValueProcessor)
+         |   `-(language::integer:6:ValueProcessor)
+         `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+             +-(language::integer:7:ValueProcessor)
+             +-(language::integer:8:ValueProcessor)
+             `-(language::integer:9:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("return a (builtin_t)")
+        {
+          std::string_view data = R"(
+let foo : builtin_t -> (builtin_t), b -> b;
+let b0 : builtin_t;
+foo(b0);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("with conversion")
+      {
+        SECTION("Return a B -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : B -> (R^1), b -> b;
+f(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a N -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : N -> (R^1), n -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a Z -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : Z -> (R^1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a R -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a B -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : B -> (R^1x1), b -> b;
+f(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a N -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : N -> (R^1x1), n -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a Z -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : Z -> (R^1x1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a R -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1x1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^2)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^2), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^3)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^3), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1x1), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^2x2)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^2x2), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^3x3)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^3x3), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+    }
+
+    SECTION("from tuple value")
+    {
+      SECTION("without conversion")
+      {
+        SECTION("Return a (B)")
+        {
+          std::string_view data = R"(
+let b:(B), b = (true, false);
+let f : Z -> (B), z -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (N)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, false);
+let f : N -> (N), n -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : N -> (N), i -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (N)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, 2, 3);
+let f : N -> (Z), i -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (Z)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, true);
+let f : Z -> (Z), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (Z)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : Z -> (Z), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, 2, 3);
+let f : Z -> (Z), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (R)")
+        {
+          std::string_view data = R"(
+let b:(B), b = (true, false);
+let f : R -> (R), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (R)")
+        {
+          std::string_view data = R"(
+let n:(N), n = (1, 2, 3);
+let f : R -> (R), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (R)")
+        {
+          std::string_view data = R"(
+let z:(Z), z = (1, -2, 3);
+let f : R -> (R), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R)")
+        {
+          std::string_view data = R"(
+let r:(R), r = (1.1, -2, 3.2);
+let f : R -> (R), x -> r;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (string)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, false);
+let f : string -> (string), t -> b;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (string)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : string -> (string), t -> n;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (string)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, -2, 3);
+let f : string -> (string), t -> z;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R), r = (1, -2, 3);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^1), r = (1, [-2], 3);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^2), r = ([1, 2], [-2, 3], [3, -4]);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^3), r = ([1, 2, 3], [0, -2, 3], [3, -4, 2]);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1x1) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^1x1), r = (1, [[-2]], 3);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2x2) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^2x2), r = ([[1, 2], [-2, 3]], [[3, -4], [2, 1]]);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3x3) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^3x3), r = ([[1, 2, 3], [0, -2, 3], [3, -4, 2]], 0);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (string)")
+        {
+          std::string_view data = R"(
+let s : (string), s = ("foo", "bar");
+let f : string -> (string), t -> s;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (R^1)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, false);
+let f : R -> (R^1), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (R^1)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : R -> (R^1), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (R^1)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, 2, 3);
+let f : R -> (R^1), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R) -> (R^1)")
+        {
+          std::string_view data = R"(
+let r : (R), r = (1, 2, 3);
+let f : R -> (R^1), x -> r;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1)")
+        {
+          std::string_view data = R"(
+let r1 : (R^1), r1 = (1, 2, 3);
+let f : R -> (R^1), x -> r1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2)")
+        {
+          std::string_view data = R"(
+let r2: (R^2), r2 = ([1, 2], [3, 4], 0);
+let f : R*R -> (R^2), (x,y) -> r2;
+f(1,2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3)")
+        {
+          std::string_view data = R"(
+let r3: (R^3), r3 = ([1, 2, 3], [1, 3, 4], 0);
+let f : R*R*R -> (R^3), (x,y,z) -> r3;
+f(1,2,3);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         `-(language::integer:3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let b: (B), b = (false, true);
+let f : R -> (R^1x1), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let n: (N), n = (1, 2, 3);
+let f : R -> (R^1x1), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let z: (Z), z = (1, -2, 3);
+let f : R -> (R^1x1), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let r: (R), r = (1, -2, 3);
+let f : R -> (R^1x1), x -> r;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1x1)")
+        {
+          std::string_view data = R"(
+let r1: (R^1x1), r1 = (1, -2, 3);
+let f : R -> (R^1x1), x -> r1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2x2)")
+        {
+          std::string_view data = R"(
+let r2: (R^2x2), r2 = ([1, -2], [3, 2], 0);
+let f : R*R*R*R -> (R^2x2), (x,y,z,t) -> r2;
+f(1,2,3,4);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3x3)")
+        {
+          std::string_view data = R"(
+let r3: (R^3x3), r3 = ([1, -2, 2], [3, 1, 2], 0);
+let f : R^3*R^3*R^3 -> (R^3x3), (x,y,z) -> r3;
+f([1,2,3],[4,5,6],[7,8,9]);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:1:ValueProcessor)
+         |   +-(language::integer:2:ValueProcessor)
+         |   `-(language::integer:3:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:4:ValueProcessor)
+         |   +-(language::integer:5:ValueProcessor)
+         |   `-(language::integer:6:ValueProcessor)
+         `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+             +-(language::integer:7:ValueProcessor)
+             +-(language::integer:8:ValueProcessor)
+             `-(language::integer:9:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("return a (builtin_t)")
+        {
+          std::string_view data = R"(
+let b0 : builtin_t;
+let bt : (builtin_t), bt = (b0, b0, b0);
+let foo : builtin_t -> (builtin_t), b -> bt;
+foo(b0);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+    }
+
+    SECTION("Return tuple in compound")
+    {
+      SECTION("Return (R^d) in compound")
+      {
+        std::string_view data = R"(
+let b0 : builtin_t;
+let f : R*R*builtin_t*R*R -> R*R^1*(R^2)*(builtin_t)*(R^3), (x,y,b,z,t) -> (t, [x], [x,y], (b,b), ([x,y,z], 0));
+f(1,2,b0,3,4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::name:b0:NameProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return (R^dxd) in compound")
+      {
+        std::string_view data = R"(
+let f : R*R*R*R -> R*R^1x1*(R^2x2)*(R^3x3), (x,y,z,t) -> (t, [[x]], (0,[[x,y],[z,t]]), ([[x,y,z], [x,x,x], [t,t,t]], 0));
+f(1,2,3,4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+    }
+
+    SECTION("errors")
+    {
+      SECTION("wrong argument number")
+      {
+        std::string_view data = R"(
+let Id : Z -> Z, z -> z;
+Id(2,3);
+)";
+
+        CHECK_AST_THROWS(data);
+      }
+
+      SECTION("wrong argument number 2")
+      {
+        std::string_view data = R"(
+let sum : R*R -> R, (x,y) -> x+y;
+sum(2);
+)";
+
+        CHECK_AST_THROWS(data);
+      }
+
+      SECTION("tuple as domain")
+      {
+        SECTION("basic type string")
+        {
+          std::string_view data = R"(
+let bad_tuple : (string) -> N, s -> 2;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"cannot use tuple (string) as a domain for user functions"});
+        }
+
+        SECTION("basic type R")
+        {
+          std::string_view data = R"(
+let bad_tuple : (R) -> R, x -> 2;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"cannot use tuple (R) as a domain for user functions"});
+        }
+      }
+
+      SECTION("invalid return implicit conversion")
+      {
+        SECTION("string -> R")
+        {
+          std::string_view data = R"(
+let bad_conv : string -> R, s -> s;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("string -> R")
+        {
+          std::string_view data = R"(
+let bad_conv : R -> R, x -> "foo";
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("string -> (R)")
+        {
+          std::string_view data = R"(
+let bad_conv : R -> (R), x -> (x, "bar");
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("string -> (R) 2")
+        {
+          std::string_view data = R"(
+let bad_conv : R -> (R), x -> "bar";
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
+let bad_B : R -> B, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : R -> (B), x -> (true, x, false);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> (B) 2")
+        {
+          std::string_view data = R"(
+let bad_B : R -> (B), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
+let next : R -> N, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> (N)")
+        {
+          std::string_view data = R"(
+let next : R -> (N), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> (N) 2")
+        {
+          std::string_view data = R"(
+let next : R -> (N), x -> (6, 2, x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
+let prev : R -> Z, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("R -> (Z)")
+        {
+          std::string_view data = R"(
+let prev : R -> (Z), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("R -> (Z) 2")
+        {
+          std::string_view data = R"(
+let prev : R -> (Z), x -> (x, 2, -3);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
+let bad_B : N -> B, n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("N -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : N -> (B), n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("N -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : N -> (B), n -> (true, n);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> B, n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("Z -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (B), n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("Z -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (B), n -> (true, n, true);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("Z -> R^2")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> R^2, n -> 1;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
+
+        SECTION("Z -> (R^2)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (R^2), n -> (n, [2,1]);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
+
+        SECTION("R^1 -> R^2")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> R^2, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+        }
+
+        SECTION("R^1 -> (R^2)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^2), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+        }
+
+        SECTION("R^1 -> (R^2)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^2), x -> ([1,2], x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+        }
+
+        SECTION("Z -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (R^3), n -> (n, [2,1,2]);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+        }
+
+        SECTION("R^1 -> R^3")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> R^3, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+        }
+
+        SECTION("R^1 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^3), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+        }
+
+        SECTION("R^1 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^3), x -> ([1,2,2], x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+        }
+
+        SECTION("R^2 -> R^3")
+        {
+          std::string_view data = R"(
+let bad_B : R^2 -> R^3, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+        }
+
+        SECTION("R^2 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^2 -> (R^3), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+        }
+
+        SECTION("R^2 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^2 -> (R^3), x -> ([1,2,5], x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+        }
+      }
+
+      SECTION("invalid argument implicit conversion")
+      {
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
+let negate : B -> B, b -> not b;
+let n : N, n = 2;
+negate(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
+let negate : B -> B, b -> not b;
+negate(3-4);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
+let negate : B -> B, b -> not b;
+negate(3.24);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
+let next : N -> N, n -> n+1;
+next(3.24);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
+let prev : Z -> Z, z -> z-1;
+prev(3 + .24);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("B -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
+        }
+
+        SECTION("N -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
+        }
+
+        SECTION("Z -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
+
+        SECTION("R -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
+        }
+
+        SECTION("B -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
+        }
+
+        SECTION("N -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
+        }
+
+        SECTION("Z -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+        }
+
+        SECTION("R -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
+        }
+
+        SECTION("B -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
+        }
+
+        SECTION("N -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
+        }
+
+        SECTION("Z -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
+        }
+
+        SECTION("R -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
+        }
+
+        SECTION("B -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
+        }
+
+        SECTION("N -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
+        }
+
+        SECTION("Z -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
+        }
+
+        SECTION("R -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+        }
+      }
+
+      SECTION("arguments invalid tuple -> R^d conversion")
+      {
+        SECTION("tuple[2] -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R, x->x[0];
+f((1,2));
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
+
+        SECTION("tuple[3] -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R, x->x[0];
+f((1,2,3));
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
+
+        SECTION("compound -> R^2")
+        {
+          std::string_view data = R"(
+let f : R*R^2 -> R, (t,x)->x[0];
+f(1,(1,2));
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
+
+        SECTION("compound -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3*R^2 -> R, (x,y)->x[0]*y[1];
+f((1,2,3),[3,4]);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
+
+        SECTION("list instead of tuple -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R, x -> x[0]*x[1];
+f(1,2,3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 1, provided 3"});
+        }
+
+        SECTION("list instead of tuple -> R^3*R^2")
+        {
+          std::string_view data = R"(
+let f : R^3*R^2 -> R, (x,y) -> x[0]*x[1]-y[0];
+f([1,2,3],2,3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 2, provided 3"});
+        }
+      }
+
+      SECTION("non pure function")
+      {
+        SECTION("argument modification")
+        {
+          SECTION("++ argument")
+          {
+            std::string_view data = R"(
+let non_pure : N -> N, x -> 3 * ++x;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("argument ++")
+          {
+            std::string_view data = R"(
+let non_pure : N -> N, x -> 1 + x ++;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("-- argument")
+          {
+            std::string_view data = R"(
+let non_pure : Z -> Z, x -> 3 * --x;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("argument --")
+          {
+            std::string_view data = R"(
+let non_pure : Z -> Z, x -> 1 + x --;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+        }
+
+        SECTION("outer variable modification")
+        {
+          SECTION("++ outer variable")
+          {
+            std::string_view data = R"(
+let a:N, a = 4;
+let non_pure : Z -> Z, x -> x * ++a;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("outer variable ++")
+          {
+            std::string_view data = R"(
+let a:N, a = 4;
+let non_pure : N -> N, x -> x + a++;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("-- outer variable")
+          {
+            std::string_view data = R"(
+let a:Z, a = 4;
+let non_pure : Z -> Z, x -> x * --a;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("outer variable --")
+          {
+            std::string_view data = R"(
 let a:Z, a = 4;
 let non_pure : Z -> Z, x -> x + a--;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
         }
       }
     }
diff --git a/tests/test_FunctionProcessor.cpp b/tests/test_FunctionProcessor.cpp
index 97cc8d6be42199f684739dba21b1b3f9a2cab644..649cfe1af8baaa683e807662cc71873af24d4073 100644
--- a/tests/test_FunctionProcessor.cpp
+++ b/tests/test_FunctionProcessor.cpp
@@ -9,6 +9,7 @@
 #include <language/ast/ASTNodeTypeCleaner.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 #include <utils/Demangle.hpp>
+#include <utils/Stringify.hpp>
 
 #include <pegtl/string_input.hpp>
 
@@ -570,6 +571,16 @@ let fx:R^3x3, fx = f(3);
                                        (TinyMatrix<3>{x, 2 * x, x * x, 3 * x, 2 + x, x - 1, x + 0.5, 2 * x - 1,
                                                       1 / x}));
     }
+
+    SECTION(" -> N*(R)")
+    {
+      std::string_view data = R"(
+let f : R -> (R)*R, x -> ((2*x, x+2, 3), x);
+let (t,x):(R)*R, (t,x) = f(3.2);
+)";
+      CHECK_FUNCTION_EVALUATION_RESULT(data, "x", double{3.2});
+      CHECK_FUNCTION_EVALUATION_RESULT(data, "t", (std::vector<double>{double{2 * 3.2}, double{3.2 + 2}, double{3}}));
+    }
   }
 
   SECTION("multi-expression functions (using R^d)")
@@ -823,6 +834,991 @@ let x:R, x = g(f(3));
     }
   }
 
+  SECTION("tuple functions")
+  {
+    SECTION("-> (B)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (B), b -> not b;
+let b:(B), b = f(true);
+)";
+        std::vector<bool> result{false};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "b", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (B), n -> (not n, n, false);
+let t:(B), t = f(true);
+)";
+        std::vector<bool> result{false, true, false};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let a:(B), a = (true, false);
+let f : N -> (B), n -> a;
+let t:(B), t = f(true);
+)";
+        std::vector<bool> result{true, false};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (N)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (N), n -> not n;
+let t:(N), t = f(false);
+)";
+        std::vector<uint64_t> result{true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (N), n -> n;
+let t:(N), t = f(7);
+)";
+        std::vector<uint64_t> result{7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (N), p -> -p;
+let t:(N), t = f(-2);
+)";
+        std::vector<uint64_t> result{2};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (N), n -> l;
+let t:(N), t = f(false);
+)";
+        std::vector<uint64_t> result{1, 0, 1};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (N), n -> l;
+let t :(N), t = f(2);
+)";
+        std::vector<uint64_t> result{2, 3, 6};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, 6, 7);
+let f : Z -> (N), p -> l;
+let t :(N), t = f(-2);
+)";
+        std::vector<uint64_t> result{2, 6, 7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (N), b -> (b, n, 12);
+let t :(N), t = f(false);
+)";
+        std::vector<uint64_t> result{false, 3, 12};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (Z)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (Z), n -> not n;
+let t:(Z), t = f(false);
+)";
+        std::vector<int64_t> result{true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (Z), n -> n;
+let t:(Z), t = f(7);
+)";
+        std::vector<int64_t> result{7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (Z), p -> p;
+let t:(Z), t = f(-2);
+)";
+        std::vector<int64_t> result{-2};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (Z), n -> l;
+let t:(Z), t = f(false);
+)";
+        std::vector<int64_t> result{1, 0, 1};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (Z), n -> l;
+let t :(Z), t = f(2);
+)";
+        std::vector<int64_t> result{2, 3, 6};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (Z), p -> l;
+let t :(Z), t = f(-2);
+)";
+        std::vector<int64_t> result{2, -6, 7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (Z), b -> (b, n, -12);
+let t :(Z), t = f(false);
+)";
+        std::vector<int64_t> result{false, 3, -12};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (R), n -> not n;
+let t:(R), t = f(false);
+)";
+        std::vector<double> result{true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (R), n -> n;
+let t:(R), t = f(7);
+)";
+        std::vector<double> result{7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (R), p -> p;
+let t:(R), t = f(-2);
+)";
+        std::vector<double> result{-2};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (R), p -> 2.3*p;
+let t:(R), t = f(-2);
+)";
+        std::vector<double> result{-2l * 2.3};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (R), n -> l;
+let t:(R), t = f(false);
+)";
+        std::vector<double> result{true, false, true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (R), n -> l;
+let t :(R), t = f(2);
+)";
+        std::vector<double> result{2ul, 3ul, 6ul};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (R), p -> l;
+let t :(R), t = f(-2);
+)";
+        std::vector<double> result{2l, -6l, 7l};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6, 7.3);
+let f : Z -> (R), p -> l;
+let t :(R), t = f(-2);
+)";
+        std::vector<double> result{2.2, -6, 7.3};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (R), b -> (b, n, -12, 2.3);
+let t :(R), t = f(false);
+)";
+        std::vector<double> result{false, 3ul, -12l, 2.3};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^1)")
+    {
+      using Rd = TinyVector<1>;
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (R^1), n -> not n;
+let t:(R^1), t = f(false);
+)";
+        std::vector<Rd> result{Rd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (R^1), n -> n;
+let t:(R^1), t = f(7);
+)";
+        std::vector<Rd> result{Rd{7}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1), p -> p;
+let t:(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1), p -> 2.3*p;
+let t:(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.3}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1), p -> 2.2*[p];
+let t:(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (R^1), n -> l;
+let t:(R^1), t = f(false);
+)";
+        std::vector<Rd> result{Rd{true}, Rd{false}, Rd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (R^1), n -> l;
+let t :(R^1), t = f(2);
+)";
+        std::vector<Rd> result{Rd{2ul}, Rd{3ul}, Rd{6ul}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (R^1), p -> l;
+let t :(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2l}, Rd{-6l}, Rd{7l}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6.1, 7.4);
+let f : Z -> (R^1), p -> l;
+let t :(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2}, Rd{-6.1}, Rd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1)")
+      {
+        std::string_view data = R"(
+let l :(R^1), l = ([2.2], -[6.1], [7.4]);
+let f : Z -> (R^1), p -> l;
+let t :(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2}, Rd{-6.1}, Rd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (R^1), b -> (b, n, -12, 2.3, [4.2]);
+let t :(R^1), t = f(false);
+)";
+        std::vector<Rd> result{Rd{false}, Rd{3ul}, Rd{-12l}, Rd{2.3}, Rd{4.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^2)")
+    {
+      using Rd = TinyVector<2>;
+      SECTION("from R^2")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2), p -> 2.2*[p, 2*p];
+let t:(R^2), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.2, -4l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2), p -> 0;
+let t :(R^2), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2)")
+      {
+        std::string_view data = R"(
+let l :(R^2), l = ([2.2, 1.2], -[6.1, 7], [7.4, 1]);
+let f : Z -> (R^2), p -> l;
+let t :(R^2), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2, 1.2}, -Rd{6.1, 7}, Rd{7.4, 1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^2), b -> ([1.2, 3], 0, [4.2, 1]);
+let t :(R^2), t = f(false);
+)";
+        std::vector<Rd> result{Rd{1.2, 3}, Rd{zero}, Rd{4.2, 1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^3)")
+    {
+      using Rd = TinyVector<3>;
+      SECTION("from R^3")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3), p -> 2.2*[p, 2*p, 2];
+let t:(R^3), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.2, -4l * 2.2, 4.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3), p -> 0;
+let t :(R^3), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3)")
+      {
+        std::string_view data = R"(
+let l :(R^3), l = ([2.2, 1.2, 2], -[6.1, 7, 5], [7.4, 1, -1]);
+let f : Z -> (R^3), p -> l;
+let t :(R^3), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2, 1.2, 2}, -Rd{6.1, 7, 5}, Rd{7.4, 1, -1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^3), b -> ([1.2, 3, 1], 0, [4.2, 1, 7]);
+let t :(R^3), t = f(false);
+)";
+        std::vector<Rd> result{Rd{1.2, 3, 1}, Rd{zero}, Rd{4.2, 1, 7}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^1x1)")
+    {
+      using Rdxd = TinyMatrix<1>;
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (R^1x1), n -> not n;
+let t:(R^1x1), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (R^1x1), n -> n;
+let t:(R^1x1), t = f(7);
+)";
+        std::vector<Rdxd> result{Rdxd{7}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1x1), p -> p;
+let t:(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1x1), p -> 2.3*p;
+let t:(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.3}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1x1")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1x1), p -> 2.2*[[p]];
+let t:(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (R^1x1), n -> l;
+let t:(R^1x1), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{true}, Rdxd{false}, Rdxd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (R^1x1), n -> l;
+let t :(R^1x1), t = f(2);
+)";
+        std::vector<Rdxd> result{Rdxd{2ul}, Rdxd{3ul}, Rdxd{6ul}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (R^1x1), p -> l;
+let t :(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2l}, Rdxd{-6l}, Rdxd{7l}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6.1, 7.4);
+let f : Z -> (R^1x1), p -> l;
+let t :(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2}, Rdxd{-6.1}, Rdxd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1x1)")
+      {
+        std::string_view data = R"(
+let l :(R^1x1), l = ([[2.2]], -[[6.1]], [[7.4]]);
+let f : Z -> (R^1x1), p -> l;
+let t :(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2}, Rdxd{-6.1}, Rdxd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (R^1x1), b -> (b, n, -12, 2.3, [[4.2]]);
+let t :(R^1x1), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{false}, Rdxd{3ul}, Rdxd{-12l}, Rdxd{2.3}, Rdxd{4.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^2x2)")
+    {
+      using Rdxd = TinyMatrix<2>;
+      SECTION("from R^2x2")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2x2), p -> 2.2*[[p, 2*p], [2,3]];
+let t:(R^2x2), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.2, -4l * 2.2, 2l * 2.2, 3l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2x2), p -> 0;
+let t :(R^2x2), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2x2)")
+      {
+        std::string_view data = R"(
+let l :(R^2x2), l = ([[2.2, 1.2],[6.1, 7]], [[7.4, 1],[3.4, -2]]);
+let f : Z -> (R^2x2), p -> l;
+let t :(R^2x2), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2, 1.2, 6.1, 7}, Rdxd{7.4, 1, 3.4, -2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^2x2), b -> ([[1.2, 3],[4.5, 6]], 0, [[4.2, 1],[1.3,-6]]);
+let t :(R^2x2), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{1.2, 3, 4.5, 6}, Rdxd{zero}, Rdxd{4.2, 1, 1.3, -6}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^3x3)")
+    {
+      using Rdxd = TinyMatrix<3>;
+      SECTION("from R^3x3")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3x3), p -> 2.2*[[p, 2*p, 2],[2,3,5],[3,6,2]];
+let t:(R^3x3), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.2, -4l * 2.2, 2l * 2.2,   //
+                                      2l * 2.2, 3l * 2.2, 5l * 2.2,     //
+                                      3l * 2.2, 6l * 2.2, 2l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3x3), p -> 0;
+let t :(R^3x3), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3x3)")
+      {
+        std::string_view data = R"(
+let l :(R^3x3), l = ([[2.2, 1.2, 2], [-6.1, 7, 5], [7.4, 1, -1]], 0);
+let f : Z -> (R^3x3), p -> l;
+let t :(R^3x3), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2, 1.2, 2,   //
+                                      -6.1, 7, 5,    //
+                                      7.4, 1, -1},
+                                 Rdxd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^3x3), b -> ([[1.2, 3, 1],[1,2,3],[4.2,2,6]], 0, [[4.2, 1, 7],[2,4,6],[2,3,1]]);
+let t :(R^3x3), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{1.2, 3, 1,   //
+                                      1, 2, 3,     //
+                                      4.2, 2, 6},
+                                 Rdxd{zero},
+                                 Rdxd{4.2, 1, 7,   //
+                                      2, 4, 6,     //
+                                      2, 3, 1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (string)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (string), n -> not n;
+let t:(string), t = f(false);
+)";
+        std::vector<std::string> result{stringify(true)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (string), n -> n;
+let t:(string), t = f(7);
+)";
+        std::vector<std::string> result{stringify(7)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> p;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(-2)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> 2.3*p;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(-2l * 2.3)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [2.3*p];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyVector<1>{-2l * 2.3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^2")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [2.3*p, 3];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyVector<2>{-2l * 2.3, 3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^3")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [1, 2.3*p, 3];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyVector<3>{1, -2l * 2.3, 3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1x1")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [[2.3*p]];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyMatrix<1>{-2l * 2.3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^2x2")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [[2.3*p, 3],[2, p]];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyMatrix<2>{-2l * 2.3, 3, 2, -2})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^3x3")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [[1, 2.3*p, 3],[1.2, 2.3, 0.5],[0.1, 1.4, 6]];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyMatrix<3>{1, -2l * 2.3, 3,   //
+                                                                1.2, 2.3, 0.5,     //
+                                                                0.1, 1.4, 6})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from string")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> "foo";
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{std::string{"foo"}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (string), n -> l;
+let t:(string), t = f(false);
+)";
+        std::vector<std::string> result{stringify(std::vector<bool>{true, false, true})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (string), n -> l;
+let t :(string), t = f(2);
+)";
+        std::vector<std::string> result{stringify(std::vector<double>{2ul, 3ul, 6ul})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (string), p -> l;
+let t :(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<double>{2l, -6l, 7l})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6, 7.3);
+let f : Z -> (string), p -> l;
+let t :(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<double>{2.2, -6, 7.3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1)")
+      {
+        using Rd = TinyVector<1>;
+
+        std::string_view data = R"(
+let l : (R^1), l = (0, [2], [3.5]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rd>{Rd{zero}, Rd{2}, Rd{3.5}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2)")
+      {
+        using Rd = TinyVector<2>;
+
+        std::string_view data = R"(
+let l : (R^2), l = ([1, 2], 0, [2, 3.5]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rd>{Rd{1, 2}, Rd{zero}, Rd{2, 3.5}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3)")
+      {
+        using Rd = TinyVector<3>;
+
+        std::string_view data = R"(
+let l : (R^3), l = ([1, 2, 3], 0, [2, 3.5, 7]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rd>{Rd{1, 2, 3}, Rd{zero}, Rd{2, 3.5, 7}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1x1)")
+      {
+        using Rdxd = TinyMatrix<1>;
+
+        std::string_view data = R"(
+let l : (R^1x1), l = ([[1]], 0, [[3.5]]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rdxd>{Rdxd{1}, Rdxd{zero}, Rdxd{3.5}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2x2)")
+      {
+        using Rdxd = TinyMatrix<2>;
+
+        std::string_view data = R"(
+let l : (R^2x2), l = ([[1,2],[3,4]], 0, [[3.5,-5],[3.7,2.9]]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{
+          stringify(std::vector<Rdxd>{Rdxd{1, 2, 3, 4}, Rdxd{zero}, Rdxd{3.5, -5, 3.7, 2.9}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3x3)")
+      {
+        using Rdxd = TinyMatrix<3>;
+
+        std::string_view data = R"(
+let l : (R^3x3), l = ([[1,2,3],[3,5,6],[9,3,4]], 0, [[1,3,7],[3.5,-5,2.3],[2.1,3.7,2.9]]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+
+        std::vector<std::string> result{stringify(std::vector<Rdxd>{Rdxd{1, 2, 3, 3, 5, 6, 9, 3, 4},   //
+                                                                    Rdxd{zero},                        //
+                                                                    Rdxd{1, 3, 7, 3.5, -5, 2.3, 2.1, 3.7, 2.9}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (string)")
+      {
+        std::string_view data = R"(
+let l:(string), l = ("foo", "bar", "foobar");
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{std::vector<std::string>{"foo", "bar", "foobar"}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (string), b -> (b, n, -12, 2.3, "foo");
+let t :(string), t = f(false);
+)";
+
+        std::vector<std::string> result{stringify(
+          std::vector<std::string>{stringify(false), stringify(3ul), stringify(-12l), stringify(2.3), "foo"})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+  }
+
   SECTION("errors")
   {
     SECTION("negative value to N")
@@ -871,5 +1867,40 @@ f(1, 4);
         CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-3)");
       }
     }
+
+    SECTION("negative value to (N)")
+    {
+      SECTION("return N -> (N)")
+      {
+        std::string_view data = R"(
+let f : N -> (N), n -> -2;
+f(2);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-2)");
+      }
+
+      SECTION("return (Z) -> (N)")
+      {
+        std::string_view data = R"(
+let z :(Z), z = (1, 2, -3);
+let f : N -> (N), n -> z;
+f(2);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-3)");
+      }
+
+      SECTION("return list")
+      {
+        std::string_view data = R"(
+let f : N*N -> (N), (m,n) -> (m, n, n-m);
+f(2, 1);
+f(1, 2);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-1)");
+      }
+    }
   }
 }
diff --git a/tools/pgs-pygments.sh b/tools/pgs-pygments.sh
index 98f90c55e028546aa8fa49fc069f06b1fe3f057a..168760cc83772fd8f35629cbf999314c320a1c91 100755
--- a/tools/pgs-pygments.sh
+++ b/tools/pgs-pygments.sh
@@ -1,4 +1,4 @@
-#!/bin/env bash
+#! /bin/bash
 
 arguments=()