diff --git a/src/language/node_processor/OStreamProcessor.hpp b/src/language/node_processor/OStreamProcessor.hpp
index 43d07364c645385f54080ae563eec41e47ae4c9d..606247653b397998fab2016b970d87637201c527 100644
--- a/src/language/node_processor/OStreamProcessor.hpp
+++ b/src/language/node_processor/OStreamProcessor.hpp
@@ -15,27 +15,7 @@ class OStreamProcessor final : public INodeProcessor
   execute(ExecutionPolicy& exec_policy)
   {
     for (size_t i = 0; i < m_node.children.size(); ++i) {
-      std::visit(
-        [&](auto&& value) {
-          using ValueT = std::decay_t<decltype(value)>;
-          if constexpr (not std::is_same_v<std::monostate, ValueT>) {
-            if constexpr (std::is_same_v<bool, ValueT>) {
-              m_os << std::boolalpha << value;
-            } else if constexpr (std::is_same_v<std::vector<EmbeddedData>, ValueT>) {
-              m_os << '(';
-              if (value.size() > 0) {
-                m_os << value[0];
-              }
-              for (size_t i = 1; i < value.size(); ++i) {
-                m_os << ", " << value[i];
-              }
-              m_os << ')';
-            } else {
-              m_os << value;
-            }
-          }
-        },
-        m_node.children[i]->execute(exec_policy));
+      m_os << m_node.children[i]->execute(exec_policy);
     }
 
     return {};
diff --git a/src/language/utils/CMakeLists.txt b/src/language/utils/CMakeLists.txt
index 3c6733cd4ae3d5c31776ad3a2328c97109d57829..6a8216a9ca62a440c7eb84111cbb87d26ae6b1d3 100644
--- a/src/language/utils/CMakeLists.txt
+++ b/src/language/utils/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_library(PugsLanguageUtils
   ASTDotPrinter.cpp
   ASTPrinter.cpp
+  DataVariant.cpp
   EmbeddedData.cpp
   )
 
diff --git a/src/language/utils/DataVariant.cpp b/src/language/utils/DataVariant.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8796833250019d10a7ab7e28b0d895c232726244
--- /dev/null
+++ b/src/language/utils/DataVariant.cpp
@@ -0,0 +1,45 @@
+#include <language/utils/DataVariant.hpp>
+
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsTraits.hpp>
+
+std::ostream&
+operator<<(std::ostream& os, const DataVariant& v)
+{
+  std::visit(
+    [&](auto&& v) {
+      using ValueT = std::decay_t<decltype(v)>;
+      if constexpr (std::is_same_v<ValueT, std::monostate>) {
+        os << "--";
+      } else if constexpr (is_vector_v<ValueT>) {
+        os << '(';
+        if (v.size() > 0) {
+          os << v[0];
+        }
+        for (size_t i = 1; i < v.size(); ++i) {
+          os << ", " << v[i];
+        }
+        os << ')';
+      } else if constexpr (std::is_same_v<bool, ValueT>) {
+        os << std::boolalpha << v;
+      } else {
+        os << v;
+      }
+    },
+    v);
+
+  return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const AggregateDataVariant& compound)
+{
+  Assert(compound.m_data_vector.size() > 0, "unexpected compound data size");
+  os << '(';
+  os << compound.m_data_vector[0];
+  for (size_t i = 1; i < compound.m_data_vector.size(); ++i) {
+    os << ", " << compound.m_data_vector[i];
+  }
+  os << ')';
+  return os;
+}
diff --git a/src/language/utils/DataVariant.hpp b/src/language/utils/DataVariant.hpp
index 0bb9a656cf5c78acf700fe9752aebb0b8a27ac17..eac6388e0226c0b2500de8e591578a87b32f2549 100644
--- a/src/language/utils/DataVariant.hpp
+++ b/src/language/utils/DataVariant.hpp
@@ -4,8 +4,8 @@
 #include <algebra/TinyVector.hpp>
 #include <language/utils/EmbeddedData.hpp>
 #include <language/utils/FunctionSymbolId.hpp>
-#include <utils/PugsAssert.hpp>
 
+#include <iostream>
 #include <tuple>
 #include <variant>
 #include <vector>
@@ -26,63 +26,25 @@ using DataVariant = std::variant<std::monostate,
                                  TinyVector<2>,
                                  TinyVector<3>>;
 
+std::ostream& operator<<(std::ostream& os, const DataVariant& v);
+
 class AggregateDataVariant   // LCOV_EXCL_LINE
 {
  private:
   std::vector<DataVariant> m_data_vector;
   bool m_is_flattenable = true;
 
-  std::ostream&
-  _printComponent(std::ostream& os, const DataVariant& value) const
-  {
-    std::visit(
-      [&](auto&& v) {
-        if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::monostate>) {
-          os << " -- ";
-        } else if constexpr (std::is_same_v<std::decay_t<decltype(v)>, std::vector<EmbeddedData>>) {
-          os << '(';
-          if (v.size() > 0) {
-            os << v[0];
-          }
-          for (size_t i = 1; i < v.size(); ++i) {
-            os << ", " << v[i];
-          }
-          os << ')';
-        } else {
-          os << v;
-        }
-      },
-      value);
-    return os;
-  }
-
-  std::ostream&
-  _print(std::ostream& os) const
-  {
-    Assert(m_data_vector.size() > 0, "unexpected compound data size");
-    os << '(';
-    this->_printComponent(os, m_data_vector[0]);
-    for (size_t i = 1; i < m_data_vector.size(); ++i) {
-      os << ", ";
-      this->_printComponent(os, m_data_vector[i]);
-    }
-    os << ')';
-    return os;
-  }
-
  public:
-  friend std::ostream&
-  operator<<(std::ostream& os, const AggregateDataVariant& compound)
-  {
-    return compound._print(os);
-  }
+  friend std::ostream& operator<<(std::ostream& os, const AggregateDataVariant& compound);
 
+  PUGS_INLINE
   void
   setIsFlattenable(bool is_flattenable)
   {
     m_is_flattenable = is_flattenable;
   }
 
+  PUGS_INLINE
   bool
   isFlattenable() const
   {
diff --git a/src/language/utils/SymbolTable.hpp b/src/language/utils/SymbolTable.hpp
index 75baa7e7303b2aa05bb0e6156932d5a00130bafc..ed4e2549f65dba1eb457aa486eba86a987d6886d 100644
--- a/src/language/utils/SymbolTable.hpp
+++ b/src/language/utils/SymbolTable.hpp
@@ -95,25 +95,7 @@ class SymbolTable
       } else if (attributes.m_data_type == ASTNodeDataType::tuple_t) {
         os << attributes.m_data_type.typeName() << ':';
       }
-      std::visit(
-        [&](auto&& value) {
-          using T = std::decay_t<decltype(value)>;
-          if constexpr (std::is_same_v<T, std::monostate>) {
-            os << "--";
-          } else if constexpr (std::is_same_v<T, std::vector<EmbeddedData>>) {
-            os << '(';
-            if (value.size() > 0) {
-              os << value[0];
-            }
-            for (size_t i = 1; i < value.size(); ++i) {
-              os << ", " << value[i];
-            }
-            os << ')';
-          } else {
-            os << value;
-          }
-        },
-        attributes.m_value);
+      os << attributes.m_value;
 
       return os;
     }