I take a somewhat different approach to defining a function to make subscripted variables in of the requested form. The algorithm doesn't differ substantially from the one used by Rod Lm; the difference lies in the way I use multiple function definitions, pattern matching, and destructuring of the formal argument sequences. SetAttributes[fancySubscript, HoldFirst] fancySubscript[var_Symbol, tag_String, index_Integer] /; Not[ValueQ[var]] := Subscript[var, tag <> ToString[index]] fancySubscript[var_Symbol, tags : {_String ..}, index_Integer] /; Not[ValueQ[var]] := fancySubscript[var, #, index] & /@ tags fancySubscript[var_Symbol, tags : {_String ..}, indices : {_Integer ..}] /; Not[ValueQ[var]] := fancySubscript[var, tags, #] & /@ indices The condition `Not[ValueQ[var]` ensures that `fancySubscript` doesn't accept variables having values. a = 42; fancySubscript[a, "tag", 2] >fancySubscript[a, "tag", 2] The overloaded definitions ensure that `fancySubscript` accepts all the following forms: fancySubscript[b, "tag", 2] > $b_{\text{tag2}}$ fancySubscript[b, {"foo", "bar", "baz"}, 2] >$\left\{b_{\text{foo2}},b_{\text{bar2}},b_{\text{baz2}}\right\}$ fancySubscript[b, {"foo", "bar", "baz"}, Range@3] >$\left( \begin{array}{ccc} b_{\text{foo1}} & b_{\text{bar1}} & b_{\text{baz1}} \\ b_{\text{foo2}} & b_{\text{bar2}} & b_{\text{baz2}} \\ b_{\text{foo3}} & b_{\text{bar3}} & b_{\text{baz3}} \\ \end{array} \right)$ ###Edit To answer the question raised in a comment by Alex, it is easy to change `fancySubscript` to do double indexing, rather than concatenating the tags and indices. The only modification required is to redefine the first definition of `fancySubscript` as fancySubscript[var_Symbol, tag_String, index_Integer] /; Not[ValueQ[var]] := Subscript[var, tag, index] With this change, for example, fancySubscript[b, {"foo", "bar", "baz"}, 2] gives >$\left\{b_{\text{foo},2},b_{\text{bar},2},b_{\text{baz},2}\right\}$