The \edef command doesn't perform assignments and your loop is full of them.
With luatex you can use expl3.
\input expl3-generic
\ExplSyntaxOn
\cs_new:Npn \range #1 #2 #3
{% #1 is the step, #2 the starting point, #3 the upper bound
#2
\int_step_function:nnnN { #2 + #1 } { #1 } { #3 } \user_addtorange:n
}
\cs_new:Nn \user_addtorange:n { , #1 }
\ExplSyntaxOff
\range{2}{3}{10}
\edef\therange{\range{2}{3}{10}}
{\tt\meaning\therange}
\bye

The function \int_step_function:nnnN has the syntax
\int_step_function:nnnN { <start> } { <step> } { <end> } <function>
where <function> should be a one argument function (macro, in plain TeX lingo) which will be passed all the integers that result by looping in the obvious way. Only integers that don't exceed <end> are passed. The function will expand to ,<current integer>. The start is added beforehand, so we have no problem with spurious commas.
A version without expl3, just for fun.
\def\range#1#2#3{% #1 = step, #2 = start, #3 = upper bound
\betterrange{#2}{#3}{#1}%
}
\def\betterrange#1#2#3{% #1 = start, #2 = upper bound, #3 = step
#1%
\ifnum\numexpr#1+#3>\numexpr#2\relax
\expandafter\gobble
\else
\expandafter\firstofone
\fi
{\expandafter,\expandafter\betterrange\expandafter{\the\numexpr#1+#3}{#2}{#3}}%
}
\def\gobble#1{}
\def\firstofone#1{#1}
\range{2}{3}{10}
\edef\therange{\range{2}{3}{10000}}
\show\therange
\bye
etexextensions you could do something like\def\range#1#2#3{\ifnum#2>#3\else#2\ifnum\numexpr#2+#1>#3\else, \fi\range{#1}{\the\numexpr#2+#1}{#3}\fi}, assuming . Are you working with Knuth's TeX? – May 15 '21 at 08:04etexis available, since I'm usingluatex. Yes, a recursive solution like yours gives no problem. But why doesn't the solution with\loopwork as well? – User May 15 '21 at 11:58