diff --git a/source/simple/support/tuple_utils/apply_for.hpp b/source/simple/support/tuple_utils/apply_for.hpp
index 504ac61e7db01ab8ff277a0b815b2c09f8255d69..fbf1d9e64c9f23a13429d987b9f6ae6939dc341f 100644
--- a/source/simple/support/tuple_utils/apply_for.hpp
+++ b/source/simple/support/tuple_utils/apply_for.hpp
@@ -3,15 +3,30 @@
 
 #include <tuple>
 #include <functional>
+#include <cassert>
 #include "../range.hpp"
 
 namespace simple::support
 {
 
+	template <typename T, typename = std::nullptr_t>
+	struct has_tuple_interface : public std::false_type {};
+
+	template <typename T>
+	struct has_tuple_interface<T, decltype
+	(
+		void(std::tuple_size<std::decay_t<T>>::value),
+		nullptr
+	) > : public std::true_type {};
+
+	template <typename T>
+	constexpr bool has_tuple_interface_v = has_tuple_interface<T>::value;
+
 	// wow this is a mess... you think it'll be optimized? O_o
-	//TODO: return a variant??
+	//TODO: return a variant
+	//TODO: rename to transform
 	template<typename F, typename First, size_t I = std::tuple_size_v<std::remove_reference_t<First>> - 1, typename... Rest>
-	[[nodiscard]] constexpr
+	constexpr
 	decltype(auto) apply_for(size_t index, F&& f, First&& first, Rest&&... rest)
 	{
 		using std::remove_reference_t;
@@ -31,9 +46,11 @@ namespace simple::support
 			return apply_for<F, decltype(forward<First_t>(first)), I - 1>(index, forward<F>(f),
 					forward<First_t>(first), forward<remove_reference_t<Rest>>(rest)...);
 
-		throw std::logic_error("simple::support::apply_for - this should never happen");
+		assert(!"simple::support::apply_for - index out of bounds");
 	}
 
+	//TODO: return a tuple of variants
+	//TODO: rename to transform
 	template<typename F, typename First, size_t I = std::tuple_size_v<std::remove_reference_t<First>> - 1, typename... Rest>
 	constexpr
 	void apply_for(range<size_t> index_range, F&& f, First&& first, Rest&&... rest)
@@ -46,6 +63,126 @@ namespace simple::support
 		}
 	}
 
+	constexpr struct tuple_void_t {} tuple_void;
+	[[nodiscard]] constexpr bool operator==(const tuple_void_t, const tuple_void_t) noexcept { return true; }
+	[[nodiscard]] constexpr bool operator!=(const tuple_void_t, const tuple_void_t) noexcept { return false; }
+
+	template <typename T>
+	constexpr decltype(auto) operator,(T&& x, const tuple_void_t)
+	//TODO: noexcept(-_-)
+	{
+		return std::forward<T>(x);
+	}
+
+	template <typename First, typename... Rest>
+	constexpr size_t min_tuple_size()
+	{
+		size_t result = std::tuple_size_v<First>;
+		if constexpr(sizeof...(Rest) != 0)
+			result = std::min(result, min_tuple_size<Rest...>());
+		return result;
+	}
+
+	template <typename... T>
+	constexpr size_t min_tuple_size_v = min_tuple_size<T...>();
+
+	template <typename... T>
+	using min_tuple_index_sequence = std::make_index_sequence<min_tuple_size_v<std::decay_t<T>...>>;
+
+	template <size_t Index, typename Range>
+	// TODO: enable_if_t<Range is integer_sequence and Index is in Range>
+	struct iteration_state
+	{
+		constexpr static std::integral_constant<decltype(Index), Index> index{};
+		constexpr static Range range{};
+	};
+	template <size_t I, typename R>
+	[[nodiscard]] constexpr bool
+	operator==(const iteration_state<I,R>, const iteration_state<I,R>)
+	noexcept { return true; }
+	template <size_t I, typename R>
+	[[nodiscard]] constexpr bool
+	operator!=(const iteration_state<I,R>, const iteration_state<I,R>) noexcept
+	{ return false; }
+
+	template<typename F>
+	constexpr decltype(auto) transform(F&& f)
+	//TODO: noexcept(-_-)
+	{
+		return std::apply(std::forward<F>(f));
+	}
+
+	template<size_t Index, typename F, typename... Tuples,
+		std::enable_if_t<(has_tuple_interface_v<Tuples> && ...)>* = nullptr,
+		std::enable_if_t<not std::is_invocable<F,
+			decltype(get<Index>(std::declval<Tuples>()))...>::value>* = nullptr,
+		std::enable_if_t<std::is_invocable<F, iteration_state<Index, min_tuple_index_sequence<Tuples...>>,
+			decltype(get<Index>(std::declval<Tuples>()))...>::value>* = nullptr >
+	constexpr
+	decltype(auto) transform(F&& f, Tuples&&... tuples)
+	//TODO: noexcept(-_-)
+	{
+		// std::invoke is not contexpr in C++17 :V
+		auto info = iteration_state<Index, min_tuple_index_sequence<Tuples...>>{};
+		return std::apply(std::forward<F>(f),
+			std::forward_as_tuple(info, get<Index>(std::forward<Tuples>(tuples))...) );
+	}
+
+	template<size_t Index, typename F, typename... Tuples,
+		std::enable_if_t<(has_tuple_interface_v<Tuples> && ...)>* = nullptr,
+		std::enable_if_t<std::is_invocable<F,
+			decltype(get<Index>(std::declval<Tuples>()))...>::value>* = nullptr >
+	constexpr
+	decltype(auto) transform(F&& f, Tuples&&... tuples)
+	//TODO: noexcept(-_-)
+	{
+		// std::invoke is not contexpr in C++17 :V
+		return std::apply(std::forward<F>(f),
+			std::forward_as_tuple(get<Index>(std::forward<Tuples>(tuples))...) );
+	}
+
+	template<typename F, typename... Tuples, size_t... Indecies,
+		std::enable_if_t<(has_tuple_interface_v<Tuples> && ...)>* = nullptr>
+	constexpr
+	decltype(auto) transform(F&& f, std::index_sequence<Indecies...>, Tuples&&... tuples)
+	//TODO: noexcept(-_-)
+	{
+		//TODO: pass the index sequence through for use in iteration_state
+		return std::tuple
+		{
+			(transform<Indecies>(std::forward<F>(f), std::forward<Tuples>(tuples)...),
+				tuple_void )
+			...
+		};
+	}
+
+	template<typename F, typename... Tuples,
+		std::enable_if_t<(has_tuple_interface_v<Tuples> && ...)>* = nullptr>
+	constexpr
+	decltype(auto) transform(F&& f, Tuples&&... tuples)
+	//TODO: noexcept(-_-)
+	{
+		return transform(std::forward<F>(f),
+			min_tuple_index_sequence<Tuples...>{} ,
+			std::forward<Tuples>(tuples)...);
+	}
+
+	namespace detail
+	{
+		template <typename T, typename Tuple, std::size_t... I>
+		constexpr T from_tuple_impl( Tuple&& t, std::index_sequence<I...> )
+		{
+			return T{get<I>(std::forward<Tuple>(t))...};
+		}
+	} // namespace detail
+
+	template <typename T, typename Tuple>
+	constexpr T from_tuple( Tuple&& t )
+	{
+		return detail::from_tuple_impl<T>(std::forward<Tuple>(t),
+			std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
+	}
+
 } // namespace simple::support
 
 #endif /* end of include guard */
diff --git a/unit_tests/tuple_utils.cpp b/unit_tests/tuple_utils.cpp
index ac7e8a23764f0540e9571b2955fe30ae1f9763e9..f1a0a46ab9a8e9827774b4d185d81f3a2b715556 100644
--- a/unit_tests/tuple_utils.cpp
+++ b/unit_tests/tuple_utils.cpp
@@ -1,9 +1,11 @@
 #include <cassert>
+#include <string>
 #include <sstream>
-#include <iostream>
 #include "simple/support/tuple_utils.hpp"
+#include "simple/support/function_utils.hpp"
 
 using namespace simple::support;
+using namespace std::literals;
 
 void ApplyFor()
 {
@@ -13,7 +15,7 @@ void ApplyFor()
 	apply_for({0,4}, [&ss](auto&& a)
 	{
 		ss << a;
-	} ,t);
+	}, t);
 	assert(ss.str() == "0 one 2 three");
 	ss.str("");
 	ss.clear();
@@ -21,7 +23,7 @@ void ApplyFor()
 	apply_for({1,3}, [&ss](auto&& a)
 	{
 		ss << a;
-	} ,t);
+	}, t);
 	assert(ss.str() == " one 2");
 	ss.str("");
 	ss.clear();
@@ -29,7 +31,7 @@ void ApplyFor()
 	apply_for(3, [&ss](auto&& a)
 	{
 		ss << a;
-	} ,t);
+	}, t);
 	assert(ss.str() == " three");
 	ss.str("");
 	ss.clear();
@@ -88,12 +90,208 @@ void CarCdr()
 		assert(tref_cdr2 == tref_cdr);
 		assert(tref_cdr2 == std::tuple(12,3));
 		assert(tref_cdr == std::tuple(12,3));
+
+		auto tref_cdr3 = tuple_tie_cdr(tref_cdr2);
+		tuple_car(tref_cdr3) = 99;
+		assert(std::get<2>(t) == 99);
+		assert(tref_cdr3 == std::tuple(99));
+
+		auto tref_cdr4 = tuple_cdr(tref_cdr2);
+		tuple_car(tref_cdr4) = 88;
+		assert(std::get<2>(t) != 88);
+		assert(tref_cdr4 == std::tuple(88));
 	}
 }
 
+void TransformBasic()
+{
+
+	assert
+	((
+		transform(std::plus<>{},
+			std::tuple{1, "2"s, 3.4}, std::tuple{4, "3"s, 2.1})
+		== std::tuple{5, "23"s, 5.5}
+	));
+
+	assert
+	((
+		transform(std::plus<>{},
+			std::array{1, 2, 3}, std::tuple{3, 2, 1})
+		== std::tuple{4, 4, 4}
+	));
+
+	assert
+	((
+		transform(std::plus<>(),
+			std::tuple{1, 2, 3.3}, std::array{3, 2, 1})
+		== std::tuple{4, 4, 4.3}
+	));
+
+	assert
+	((
+		transform(std::plus<>{},
+			std::tuple{1, 2, 3.3, 4, 5}, std::array{3, 2, 1})
+		== std::tuple{4, 4, 4.3}
+	));
+
+	assert
+	((
+		transform
+		(
+			[](auto... x) { return (x + ...); },
+			std::tuple{"1"s, 2},
+			std::tuple{"3"s, 4},
+			std::tuple{"5"s, 6}
+		)
+		== std::tuple{"135"s, 12}
+	));
+
+}
+
+struct counter
+{
+	static size_t inst;
+	static size_t copy;
+	static size_t move;
+
+	counter() { ++inst; };
+	counter(const counter&) {  ++copy; }
+	counter(counter&&) {  ++move; }
+};
+size_t counter::inst = 0;
+size_t counter::copy = 0;
+size_t counter::move = 0;
+
+void TransformCopyCount()
+{
+
+	transform([](auto&& x) { return std::forward<decltype(x)>(x); }, std::tuple{counter{}, counter{}, counter{}} );
+	assert(3 == counter::inst);
+	assert(0 == counter::copy);
+	assert(0 < counter::move);
+
+}
+
+void TransformReturnVoid()
+{
+
+	assert
+	((
+		transform([](auto...){},
+			std::tuple{1, 2, 3.3}, std::array{3, 2, 1})
+		== std::tuple{tuple_void, tuple_void, tuple_void}
+	));
+
+	assert
+	((
+		transform
+		(
+			overloaded
+			{
+				[](std::string, int){},
+				std::plus<>{},
+			},
+			std::tuple{1, "2"s, "3"s},
+			std::tuple{3, 2,    "1"s}
+		)
+		== std::tuple{4, tuple_void, "31"s}
+	));
+
+}
+
+template <size_t I, size_t S>
+constexpr iteration_state<I,std::make_index_sequence<S>> i_state{};
+
+struct
+{
+	template <size_t Index, typename Range, typename... Values>
+	auto operator()(iteration_state<Index,Range>, Values... v)
+	{
+		return (Range::size() - Index) * (v + ...);
+	}
+} sum_mul_index;
+
+void TransformIterationState()
+{
+
+	assert
+	((
+		transform([](auto i, auto, auto) { return i; },
+			std::tuple{1, 2, 3.3}, std::array{3, 2, 1})
+		== std::tuple{i_state<0,3>, i_state<1,3>, i_state<2,3>}
+	));
+
+	assert
+	((
+		transform([](auto i, auto, auto) { return i; },
+			std::tuple{1, 2, 3.3, 4, 5}, std::array{3, 2, 1})
+		== std::tuple{i_state<0,3>, i_state<1,3>, i_state<2,3>}
+	));
+
+	assert
+	((
+		transform(sum_mul_index,
+			std::tuple{1, 2, 3.3}, std::array{3, 2, 1, 0, -1, -2})
+		== std::tuple{4*3,4*2,4.3*1}
+	));
+
+}
+
+struct unlike_tuple {};
+template <typename F>
+std::string transform(F&&, const unlike_tuple&)
+{return "Don't be too greedy!"; }
+
+struct like_tuple
+{
+	int a;
+	std::string b;
+
+	bool operator==(const like_tuple& other) const
+	{ return a == other.a && b == other.b; }
+};
+
+template <>
+struct std::tuple_size<like_tuple> : public std::integral_constant<size_t, 2> {};
+
+template <size_t I, typename T,
+	std::enable_if_t<std::is_same_v<std::decay_t<T>,like_tuple>>* = nullptr>
+decltype(auto) get(T&& t) noexcept
+{
+	static_assert(I < 2);
+	if constexpr (I == 0)
+	{
+		return std::forward<T>(t).a;
+	}
+	else
+	{
+		return std::forward<T>(t).b;
+	}
+}
+
+void TransformNonTuple()
+{
+
+	assert
+	((
+		from_tuple<like_tuple>(transform(
+			std::plus<>{}, like_tuple{1,"1"s}, std::tuple{2,"2"s} ))
+		==
+		like_tuple{3, "12"s}
+	));
+
+	assert( transform([](){ return "Too greedy!"; }, unlike_tuple{}) == "Don't be too greedy!" );
+
+}
+
 int main()
 {
 	ApplyFor();
 	CarCdr();
+	TransformBasic();
+	TransformCopyCount();
+	TransformReturnVoid();
+	TransformIterationState();
+	TransformNonTuple();
 	return 0;
 }