Home  /  Resources & support  /  FAQs  /  Syntax for allowing alternative types of arguments to program options
Note: This FAQ is based on a question and answer that appeared on Statalist.

How do I signal the alternative types of arguments to program options?

Title   Syntax for allowing alternative types of arguments to program options
Authors Nicholas Winter, University of Virginia
Michael Blasnik, Blasnik & Associates
Nicholas J. Cox, Durham University, UK

Question:

I want to use syntax with an option, call it x1(), which will be allowed to be either a real number or a numeric variable. Is there an easy way to do this?

Answer:

Concretely, these two forms should be combined into one:

        syntax, x1(real)
        syntax, x1(varname numeric)

The short answer is that there are several ways to do it. Below, we outline and exemplify three solutions. For background, see help syntax, capture, and confirm or [P] syntax, [P] capture, and [P] confirm.

Solution 1: Specify a string argument and examine its contents afterwards

Get x1() as a string, and then test it after your syntax line:

        syntax , x1(string)

        capture confirm variable `x1'
        if _rc {
                capture confirm number `x1'
                if _rc {
                        di as error "x1() must be a new variable name or a number"
                        error 198
                }
                else local isvar 0
        }
        else local isvar 1

This also sets the local macro isvar to indicate if x1() specified a variable name or a number in case you need to take different action for each.

If the syntax included other elements, they can be included in the syntax statement as usual.

Solution 2: Use two alternative syntaxes together with capture

        capture syntax , x1(real)

        if _rc capture syntax, x1(varname numeric)
        else local isvar 0
        
        if _rc {
                di as err "x1() must be a new variable name or a number"
                exit 198
        }
        else local isvar 1

That is, if one syntax does not fit, the other one must.

This also sets the local macro isvar to indicate if x1() specified a variable name or a number in case you need to take different action for each.

If the syntax included other elements,

        capture syntax ... , x1(real) ...

        if _rc {
                syntax ... , x1(varname numeric) ...
                local isvar 1
        }
        else local isvar 0

If the problem is with what was in x1(), then the user will get a message to that effect. If the problem lies with other stuff, then there will be a message to that effect. The only loss is a little informativeness.

Solution 3: Use two options with the same abbreviation

To get two types of behavior from the same option, type

        syntax  [, X1a(real 0) X1b(varname numeric)]

        local nopts = (`x1a' != 0) + ("`x1b'" != "")
        
        if `nopts' == 0 {
                di as err "x1() option required"
                exit 198
        }
        else {
                local isvar = "`x1b'" != ""
                local x1 = cond(`isvar', "`x1b'", "`x1a')
        }

This also sets the local macro isvar to indicate if x1() specified a variable name or a number in case you need to take different action for each.

The online help should just list a single option named x1(), flagging the fact that different types of arguments are acceptable. Here, you are exploiting that Stata allows option names to have the same abbreviation, as long as the full option name differs. syntax will then parse what the user types as you wish.

Note that since x1a() is not a required option, it will return a default value (0 in the example). In some problems, only certain values make sense (say, positive values). Setting a default value that makes no sense allows you to distinguish between what the user typed and the default. Trickier circumstances can be dealt with by specifying that x1a() takes a numlist with particular properties.

Alternatively, if there is a natural default number for x1() (again, for example, 0), then the code can be based on

        syntax  [, X1a(real 0) X1b(varname numeric)]

        local isvar = "`x1b'" != ""
        local x1 = cond(`isvar', "`x1b'", "`x1a')

However, as above, because two options are allowed, it is possible that a user might specify both of them, presumably by accident, and careful code will trap this.

If the syntax includes other elements, they can be included in the syntax statement as usual.

Note:   The order of the authors’ names was determined by a random shuffle in Stata of their surnames from alphabetical order after set seed 123456789.