I agree with Werner that multiple optional arguments should be replaced by a key-value syntax. Here's a way:
\documentclass{article}
\usepackage{enumerate,multicol,xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{newenv}{ O{} }
{
\keys_set:nn { aksr/newenv }
{
#1 % use the options
}
\int_compare:nT { \l__aksr_columns_tl > 1 } % more than one column
{ \begin{multicols}{ \l__aksr_columns_tl } }
% We must pass the optional argument expanded
\use:x { \exp_not:N \begin{enumerate}[ \l__aksr_label_tl ] }
}
{
\end{enumerate}
\int_compare:nT { \l__aksr_columns_tl > 1 }
{ \end{multicols} }
}
\tl_new:N \l__aksr_columns_tl
\tl_new:N \l__aksr_label_tl
\keys_define:nn { aksr/newenv }
{
columns .tl_set:N = \l__aksr_columns_tl,
columns .initial:n = 2,
label .tl_set:N = \l__aksr_label_tl,
label .initial = 1.,
}
\ExplSyntaxOff
\begin{document}
\begin{newenv}
\item a
\item b
\item c
\item d
\end{newenv}
\hrule
\begin{newenv}[columns=1]
\item a
\item b
\item c
\item d
\end{newenv}
\hrule
\begin{newenv}[label=i)]
\item a
\item b
\item c
\item d
\end{newenv}
\hrule
\begin{newenv}[label=(A),columns=3]
\item a
\item b
\item c
\item d
\item e
\item f
\end{newenv}
\end{document}
It's quite straightforward, once we have some hand with it. The only subtlety is that we must pass the argument to \begin{enumerate}
expanded and not the token list variable, so \use:x
comes to rescue.
The two keys just store the value in a token list variable for later usage. The defaults are simply established by giving the values before evaluating the optional argument to newenv
.
You may consider adopting enumitem
rather than enumerate
.

Here's a different version that works with enumitem
and shows the use of "unknown" keys:
\documentclass{article}
\usepackage{enumitem,multicol,xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{newenv}{ O{} }
{
% clear the options passed to enumitem
\tl_clear:N \l__aksr_enumitem_tl
\keys_set:nn { aksr/newenv }
{
#1 % use the options
}
\int_compare:nT { \l__aksr_columns_tl > 1 } % more than one column
{ \begin{multicols}{ \l__aksr_columns_tl } }
% We must pass the optional argument expanded; we append
% the label key and value to the other possibly set options
\tl_put_right:Nn \l__aksr_enumitem_tl { label = }
\tl_put_right:NV \l__aksr_enumitem_tl \l__aksr_label_tl
\aksr_beginenum:V \l__aksr_enumitem_tl
}
{
\end{enumerate}
\int_compare:nT { \l__aksr_columns_tl > 1 }
{ \end{multicols} }
}
\cs_new_protected:Npn \aksr_beginenum:n #1
{
\begin{enumerate}[#1]
}
\cs_generate_variant:Nn \aksr_beginenum:n {V}
\tl_new:N \l__aksr_columns_tl
\tl_new:N \l__aksr_label_tl
\tl_new:N \l__aksr_enumitem_tl
\keys_define:nn { aksr/newenv }
{
columns .tl_set:N = \l__aksr_columns_tl,
columns .initial:n = 2,
label .tl_set:N = \l__aksr_label_tl,
label .initial:n = \arabic*.,
%% unknown keys will be passed to enumitem
unknown .code:n = \tl_put_right:Nx \l__aksr_enumitem_tl
{
\l_keys_key_tl % the key name
\tl_if_empty:nF { #1 } % the value, if not empty
{ = \exp_not:n { #1 } } , },
}
\ExplSyntaxOff
\begin{document}
\begin{newenv}
\item a
\item b
\item c
\item d
\end{newenv}
\hrule
\begin{newenv}[columns=1,noitemsep]
\item a
\item b
\item c
\item d
\end{newenv}
\hrule
\begin{newenv}[label=\roman*)]
\item a
\item b
\item c
\item d
\end{newenv}
\hrule
\begin{newenv}[label=(\alph*),columns=3]
\item a
\item b
\item c
\item d
\item e
\item f
\end{newenv}
\end{document}
\newenvironment
(like\newcommand
and friends) accepts only one optional argument. For multiple optional arguments, consider thexparse
interface:\NewDocumentEnvironment{newenv}{o o}{<beg env>}{<end env>}
. However, with multiple optional arguments, the only way to specify the second, would be to also specify the first, even if it could be empty. How else would the parser know that the only optional argument is the second? In such instances, a key-value approach is preferred.newcommand
, but the syntax is pretty much identical fornewenvironment