Index: src/GenPoly/InstantiateGeneric.cc
===================================================================
--- src/GenPoly/InstantiateGeneric.cc	(revision f2cdc447e88a1db49f6eba0f7b1fc44c3b139f91)
+++ src/GenPoly/InstantiateGeneric.cc	(revision fc191295fcc2cb05fb92c6aefaf097781b9fa217)
@@ -255,5 +255,5 @@
 		}
 
-		assert( baseParam == baseParams.end() && param == params.end() && "Type parameters should match type variables" );
+		assertf( baseParam == baseParams.end() && param == params.end(), "Type parameters should match type variables" );
 		return gt;
 	}
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision f2cdc447e88a1db49f6eba0f7b1fc44c3b139f91)
+++ src/ResolvExpr/Unify.cc	(revision fc191295fcc2cb05fb92c6aefaf097781b9fa217)
@@ -134,5 +134,5 @@
 		  case TypeDecl::Ftype:
 			return isFtype( type, indexer );
-			case TypeDecl::Ttype:
+		  case TypeDecl::Ttype:
 			// ttype unifies with any tuple type
 			return dynamic_cast< TupleType * >( type ) || Tuples::isTtype( type );
@@ -592,12 +592,57 @@
 		for ( ; it != params.end() && jt != otherParams.end(); ++it, ++jt ) {
 			TypeExpr *param = dynamic_cast< TypeExpr* >(*it);
-			assert(param && "Aggregate parameters should be type expressions");
+			assertf(param, "Aggregate parameters should be type expressions");
 			TypeExpr *otherParam = dynamic_cast< TypeExpr* >(*jt);
-			assert(otherParam && "Aggregate parameters should be type expressions");
-
-			if ( ! unifyExact( param->get_type(), otherParam->get_type(), env, needAssertions, haveAssertions, openVars, WidenMode(false, false), indexer ) ) {
+			assertf(otherParam, "Aggregate parameters should be type expressions");
+			
+			Type* paramTy = param->get_type();
+			Type* otherParamTy = otherParam->get_type();
+
+			bool tupleParam = Tuples::isTtype( paramTy );
+			bool otherTupleParam = Tuples::isTtype( otherParamTy );
+
+			if ( tupleParam && otherTupleParam ) {
+				++it; ++jt;  // skip ttype parameters for break
+			} else if ( tupleParam ) {
+				// bundle other parameters into tuple to match
+				TupleType* binder = new TupleType{ paramTy->get_qualifiers() };
+
+				do {
+					binder->get_types().push_back( otherParam->get_type()->clone() );
+					++jt;
+
+					if ( jt == otherParams.end() ) break;
+
+					otherParam = dynamic_cast< TypeExpr* >(*jt);
+					assertf(otherParam, "Aggregate parameters should be type expressions");
+				} while (true);
+
+				otherParamTy = binder;
+				++it;  // skip ttype parameter for break
+			} else if ( otherTupleParam ) {
+				// bundle parameters into tuple to match other
+				TupleType* binder = new TupleType{ otherParamTy->get_qualifiers() };
+
+				do {
+					binder->get_types().push_back( param->get_type()->clone() );
+					++it;
+
+					if ( it == params.end() ) break;
+
+					param = dynamic_cast< TypeExpr* >(*it);
+					assertf(param, "Aggregate parameters should be type expressions");
+				} while (true);
+
+				paramTy = binder;
+				++jt;  // skip ttype parameter for break
+			}
+
+			if ( ! unifyExact( paramTy, otherParamTy, env, needAssertions, haveAssertions, openVars, WidenMode(false, false), indexer ) ) {
 				result = false;
 				return;
 			}
+
+			// ttype parameter should be last
+			if ( tupleParam || otherTupleParam ) break;
 		}
 		result = ( it == params.end() && jt == otherParams.end() );
