I often want to write functions that take as an argument either a) a rule or b) a list of rules. As an example, when using Replace with just one rule, it does not matter whether you give it as a list or not:

Replace[Range[10], 7->"seven", {1}]
Replace[Range[10], {7->"seven"}, {1}]

will both work as expected.

Now, if I wanted to set this up, I would use the listed form throughout the function body, and add a second function pattern to actually rewrite the non-listed Rule into a List:

Replace[expr_, rules_Rule, levelspec_] := Replace[expr, {rules}, levelspec]
Replace[expr_, rules_List, levelspec_] := the function body...

Is there a way to write a pattern which matches both forms and returns the listed one?

It is, of course, not too hard to write the function body in a way that it supports both forms. But actually I would like to have it done in the pattern already, so I will not have to take special care of it and can easily extend existing functions to support this feature.

  • 1
    $\begingroup$ I use the same approach that you describe. I think it's the best. Of course it's very difficult to prove that something is not possible, so let's wait :-) It is possible to use a single pattern as in _Rule | {__Rule} but this will shift handling the list-vs-nonlist problem elsewhere, so it doesn't simplify things. I prefer the solution you describe, i.e. f[r_Rule] := f[{r}]. $\endgroup$
    – Szabolcs
    Jun 20, 2014 at 14:53
  • $\begingroup$ Well, when I try to use x_Rule|x__Rule, I get attern::patv: Name x used for both fixed and variable length patterns. as an error. $\endgroup$
    – Theo Tiger
    Jun 20, 2014 at 14:58
  • $\begingroup$ Exactly. You need to use r: (_Rule | {__Rule}) then in the body of your function check if r is a list of rules or a single rule. This is a single pattern, but it makes things more complicated. This is why I think that your solution is the best alternative. $\endgroup$
    – Szabolcs
    Jun 20, 2014 at 15:00
  • $\begingroup$ You may be able to use @Szabolcs's pattern and just wrap it with a list by default if you are using the rules for pattern matching. Mathematica will unwrap lists of rules in most situations, but not always. Try: Range@10 /. {7 -> "seven", 8 -> "eight"} and Range@10 /. {{7 -> "seven", 8 -> "eight"}} and Range@10 /. {{{7 -> "seven", 8 -> "eight"}}} for example. $\endgroup$
    – mfvonh
    Jun 20, 2014 at 16:00
  • 1
    $\begingroup$ The patv message is not an error. It is simply a warning. The definition is still made as requested and you can choose to ignore the message if you wish. $\endgroup$ Jun 21, 2014 at 12:38

2 Answers 2


Since you are specifically interested in pattern matching rather than functional equivalents such as the one presented by eldo here is an additional answer.

Szabolcs already gave my preferred solution, which is to use Alternatives, though he did not recommend it. Nevertheless I do. As complete code for reference:

foo[rules : _Rule | {__Rule}] := bar[rules]

foo[1 -> 2]

foo[{1 -> 2, "a" -> "b"}]
bar[1 -> 2]

bar[{1 -> 2, "a" -> "b"}]

You can usually use this as-is because as you note System functions are designed to accept both forms. If for whatever reason you need to "use the listed form throughout the function body" it's as simple as:


foo[rules_Rule | {rules___Rule}] := bar[{rules}]

foo[1 -> 2]

foo[{1 -> 2, "a" -> "b"}]
bar[{1 -> 2}]

bar[{1 -> 2, "a" -> "b"}]

Note that I turned off the Pattern::patv Message. As Oleksandr correctly notes that Message does not indicate an error but merely serves to indicate a possible mistake; I see it as little more than "training wheels" for new users. Once you understand what it is warning against I suggest you turn it off as it may otherwise keep you from using Mathematica patterns to their full potential.

A final note: don't forget to allow for RuleDelayed rules in your function definitions if you wish to make them general.

Recommended reading:

A simpler question of the same type:

  • $\begingroup$ Good explanation, thank you! $\endgroup$
    – Theo Tiger
    Nov 6, 2014 at 10:49
  • $\begingroup$ @Theo You're welcome. Thanks for the Accept. $\endgroup$
    – Mr.Wizard
    Nov 6, 2014 at 10:50
  • $\begingroup$ @Theo I added links to a couple of earlier answers of mine that I think you should read as well. $\endgroup$
    – Mr.Wizard
    Nov 6, 2014 at 11:10
replace[expr_, levelspec_, rules__] := Replace[expr, List @ rules, levelspec]

replace[Range @ 10, {1}, 7 -> "seven"]
replace[Range @ 10, {1}, 7 -> {"seven"}]

replace[Range @ 10, {1}, 7 -> "seven", 8 -> "eight"]
replace[Range @ 10, {1}, {7 -> "seven", 8 -> "eight"}]

All give the expected results.

  • $\begingroup$ I didn't edit your bracketing. That edit was made by YOU. Look at the edit history. $\endgroup$
    – m_goldberg
    Jun 20, 2014 at 22:35
  • $\begingroup$ @m_goldberg - So sorry, you're right $\endgroup$
    – eldo
    Jun 20, 2014 at 22:44
  • $\begingroup$ Thank you for your contribution. However, your code does not address the essential part of the question, i.e. how to support listed and non-listed arguments in a simple manner. $\endgroup$
    – Theo Tiger
    Jun 21, 2014 at 14:35

Not the answer you're looking for? Browse other questions tagged or ask your own question.