You can make your code very defensive in Mathematica if you are willing put in the effort it takes. When writing code that I expect will get a lot of use over a long period, both by me and others, I take pains to make the exposed function reasonably fool-proof.
When I'm worried about being passed invalid arguments, I almost always take one of two routes.
Give my function the HoldAll
attribute and validate the arguments myself int the function body. This is the method of choice when I want complete control over the error messages issued. I am likely to explicitly return $Failure after issuing an error message.
Require the given arguments to match stringent argument patterns. This let's me draw on Mathematica's powerful pattern matching language to protect my function from bad calls.
Here are examples drawn from a turtle graphics package I have written. They mainly use the second approach, but the first one also does some internal validation because it processes options that use string tags to identify the option chosen.
turtleGraphics::"badargs" = "`1` doesn't know what to do with `2`";
turtleGraphics::"badopt" = "`1` isn't a valid `2`";
Options[draw] = {"graphics" -> True, "reset" -> True};
draw[t_Symbol, opts : OptionsPattern[{draw, Graphics}]] :=
Module[{clrF, postF},
clrF =
If[OptionValue["reset"],
clearTrack,
Identity,
Message[turtleGraphics::"badopt",
Style[{"reset" -> OptionValue["reset"]}, "SBO"], "drawing option"];
Return @ $Failed];
Switch[postF = OptionValue["graphics"],
True | Graphics,
Graphics[render[t, clrF], Evaluate[FilterRules[{opts}, Options[Graphics]]]],
False | Identity, render[t, clrF],
_Symbol | _Function, postF @ render[t, clrF],
_, Message[turtleGraphics::"badopt",
Style[{"graphics" -> OptionValue["graphics"]}, "SBO"], "drawing option"];
$Failed]]
draw[opts : OptionsPattern[]] := draw[$turtle, opts]
draw[args : __] :=
Message[turtleGraphics::"badargs", "draw", Style[Defer @ {args}, "SBO"]]
This second example taken from the same package uses a fairly fussy pattern to reject invalid color specifications.
validColor = (_RGBColor | _GrayLevel | _Hue);
pc[t_Symbol, c : validColor] :=
(If[isTurtle[t],
setColor[t, colorToRGB[c]],
Message[turtleGraphics::"notsymb", "pc"]];)
pc[c_] := pc[$turtle, c]
pc[args : ___] :=
Message[turtleGraphics::"badargs", "pc", Style[Defer @ {args}, "SBO"]]
Sorry that I couldn't come up simpler examples, but functions thst need this kind argument or option validation tend more than a couple of lines. Note that draw
expends more code on validation than on doing "real" work. It hands off the work to render
which doesn't do validation because it was implemented under the expectation that any needed validation has already been done. render
is not exposed for public use.
update
Here is a quotation from turtle graphics documentation. I add to make draw
option processing more clear.
draw
returns a turtle's track as a graphics object or as a list of graphics primitives. What is returned is controlled by the option "graphics"
.
"graphics" -> True
or "graphics" -> Graphics
. When either of these options is given, a graphics object is returned; this is the default. In this case, draw
will also accept any options accepted by Graphics
and pass them on when it calls Graphics
. In those cases, when the "graphics"
option is used to turn off this behavior, any graphics options will be ignored.
"graphics" -> False
or "graphics" -> Identity
. When either of these options is given, a list of graphics primitives is returned. This second option is useful when the intent is to combine the turtle track with other graphics primitives as part as a larger graphics object. n this case, draw
will not accept any further options.
"graphics" -> f_Symbol
or "graphics" -> f_Function
. When an option of one of these two forms is given, the output of draw
will be f[render[...]]
. It should be clear that f
must evaluate to a function of one variable.
This third option form is supplied strictly for convenience; there is nothing that can be accomplished by using this form that can't be accomplished by post-processing the list returned when "graphics" -> False is given.
Side effects. If the turtle's pen is down when draw
is called, the turtle's pen is raised and this has additional effects (see pu
).
draw
has an additional option, "reset"
, with the default value, True
, meaning the turtle's track will be cleared when draw
completes; when the alternative value, False
, is given, the track will be preserved.