60
$\begingroup$

It is my practice to place Condition expressions on the left side of := and :> in almost every case.

  1. I find this to be more logical as it is part of the pattern

    With the exception of use inside Module, Block, or With on the RHS, which is a special case, the Condition depends only on the LHS, and therefore IMHO is more logically placed on the LHS

  2. Its behavior remains consistent when used with = and ->

    • f[x_] /; x < 5 := 1 and g[x_] /; x < 5 = 1 behave similarly
    • f[x_] := 1 /; x < 5 and g[x_] = 1 /; x < 5 behave differently
  3. The evaluation path is significantly less complicated

    Placing the condition on the RHS requires the internal use of RuleCondition and $ConditionHold which can significantly slow down simple functions.

    Clear[f, g]
    
    f[x_] /; OddQ[x] := 1
    f[x_] := 0;
    
    g[x_] := 1 /; OddQ[x]
    g[x_] := 0;
    
    f[4] //Trace
    
    {f[4], {OddQ[4], False}, 0}
    
    g[4] //Trace
    
    {g[4],{{OddQ[4],False},RuleCondition[$ConditionHold[$ConditionHold[1]],False],Fail},0}
    
    a = Range@1*^6;
    
    Timing[f /@ a;]
    Timing[g /@ a;]
    
    {0.421, Null}
    {0.655, Null}
    

Nevertheless, the documentation for Condition shows the RHS form and many experienced users also seem to favor this form.

Mathematica graphics

Which form should be standard, and why?


A brief edit: The form f[x_ /; x < 5] := 1 is what I use most often as should be clear to those who read my answers on StackOverflow. I omitted this form specifically because I didn't want to spawn a discussion (bad for SE sites) about purely-stylistic differences. I see now that this may have had the opposite effect. Rather I wish to focus this question on the apparently canonical yet IMHO inferior RHS placement and what its merits are.

$\endgroup$
0

5 Answers 5

48
$\begingroup$

I prefer the Condition to appear on the left-hand-side and outside the square brackets for several reasons.

Type signature

I often think of the condition as (part of) the analog of the signature in a typed language, so it should go on the left hand side.

Order of operations

I like that the elements of the function definition appear in the order in which I want them to happen:

f[x_] /; x > 0 := Sqrt[x]
  1. Look for f[x_].
  2. Check that x > 0.
  3. Return Sqrt[x].
  4. (Optional) Check any postcondition (see below).

Function contract

When an argument-checking definition of the form

f[else___] := Throw["Error in f."]

appears, a left-hand-side Condition often plays the role of a precondition in the sense of Design By Contract. A Condition can also appear on the right-hand-side and this plays the role of a postcondition:

f[x_] /; x > 0 := Sqrt[x] /; Sqrt[x] > 0

Consistency of appearance

I prefer f[x_] /; x > 0 to the alternative f[x_ /; x > 0] for consistency, because sometimes placing the Condition inside the square brackets is not possible, such as when the Condition depends on multiple arguments:

f[x_, y_] /; x > y := 1/(x - y)

Update: Rationale

I think Brett's preference of putting the Condition as close as possible to the quantity to which it applies is equally good so I want to explain why I ended up with my slightly different preference.

Basically I was writing a sequence of definitions like this, following Brett's guideline:

f[x_ /; c1[x], y_] := this
f[x_, y_ /; c2[y]] := that
f[x_, y_] /; c3[x, y] := other

Note that all of these define f[x, y]. So there are two things I didn't like about that:

  1. The key difference between each LHS is the different conditions on x and y, and these are difficult to read quickly here because they all start at different places and are mixed in with f[x_, y_].
  2. When a condition needs to change such that it suddenly starts or stops depending on x or y, I need to move it from inside the square brackets to outside or vice versa.

Now compare:

f[x_, y_] /; c1[x] := this
f[x_, y_] /; c2[y] := that
f[x_, y_] /; c3[x, y] := other

Of course, what would make even more sense would be to adhere to Brett's guideline except in special cases like above! Maybe I will try that now ...

$\endgroup$
3
  • 2
    $\begingroup$ +1 Since you work at Wolfram Research, do you have any thought about why only the RHS form is shown in the documentation of Condition? $\endgroup$
    – Mr.Wizard
    Jan 23, 2012 at 15:57
  • 4
    $\begingroup$ @Mr.Wizard In the documentation for Condition, the very first case "patt /; test" is actually the LHS form of Condition. For example patt could be x_ or f[x_]. The other two are the RHS form. $\endgroup$ Jan 24, 2012 at 2:32
  • $\begingroup$ Very nice write up. Personally I keep the practice of keeping conditions on a single variable next to it, simply because I like to think of it as a type or interface. So some functions read h[x_List] and others will read h[x_/;x>4]. But of cause there are cases of mixed patterns as you note, which must be moved outside. $\endgroup$
    – jVincent
    Jun 15, 2012 at 0:07
21
$\begingroup$

Another reason to use the LHS placement is mentioned in passing by Robby Villegas in the devcon99 presentation Working with Unevaluated Expressions:

I recommend that you hang the condition /; ... off the lhs, not the rhs, because then you can revoke the definition by plopping the whole lhs into an Unset command

For example:

f[x_] /; foo := 2 x  (* imagine a long definition *)
f[x_] /; bar := 5 x

We can clear one of the definitions using only the LHS:

Unset[f[x_] /; foo]

Definition[f]
f[x_] /; bar := 5 x

If using the RHS placement this Unset does not work:

g[x_] := 2 x /; foo
g[x_] := 5 x /; bar

Unset[g[x_] /; foo]

During evaluation of In[3]:= Unset::norep: Assignment on g for g[x_]/;foo not found. >>

$Failed
$\endgroup$
20
$\begingroup$

My general preference is to put the condition as close as possible to the quantity to which it applies:

... to a single argument:

f[x_ /; x > 2, y_] := x + y

{f[1, 2], f[3, 2]}
{f[1, 2], 5}

... to a relationship between arguments:

g[x_, y_] /; x > y := x + y

{g[1, 2], g[3, 2]}
{g[1, 2], 5}

... to a value calculated during the evaluation of the function:

h[x_, y_] := Module[{z = x + y}, z^2 /; z > 3]

{h[1, 2], h[3, 2]}
{h[1, 2], 25}
$\endgroup$
1
  • $\begingroup$ I think this is a very sensible policy though my answer proposed a different one. $\endgroup$ Jan 24, 2012 at 2:39
14
$\begingroup$

Style is a matter of taste and education. There is not a definite answer, only a personal answer. Having said that, there is another way for the condition that is common in code:

h[x_ /; OddQ[x]] := 1;
h[x_] := 0
h[4] // Trace

My personal preference is your f[] style, since I find that a good compromise between readability and closeness to the actual symbol involved. Imagine a long Module, then you'd have to scroll quite a bit to get/find the condition. The benefit of the RHS method is readability for people.

$\endgroup$
3
  • 1
    $\begingroup$ This is actually the form I prefer. $\endgroup$
    – Heike
    Jan 23, 2012 at 12:18
  • $\begingroup$ I use this form as well, preferring to keep conditions close to the pattern they apply to, but I was trying to not complicate things in my question. $\endgroup$
    – Mr.Wizard
    Jan 23, 2012 at 12:33
  • $\begingroup$ I have updated the question with a small note attempting to focus the topic on the RHS placement. $\endgroup$
    – Mr.Wizard
    Jan 23, 2012 at 12:40
10
$\begingroup$

My preference is the same as Andrew's, for the consistency and layout reasons he specified. That is, put the precondition Condition after the function's pattern, f[x_, y_] /; test[x,y] := ... - even if test only depends on a single variable/blank.

But I'd like to add an extra reason for not putting the Condition inside the function brackets, which is anytime you can do that, you could use a PatternTest (?) instead. That is, instead of

f[x_/;test[x], y] := ...

you could and maybe should use

f[x_?test, y] := ...

As for putting the condition on the LHS of the rule/definition vs the RHS, I once again agree with Andrew and with Mr.Wizard's comments in the question.

$\endgroup$
1
  • 3
    $\begingroup$ I favor x_ /; x<5 over x_?(#<5&) for clarity. If using a named test function I agree that PatternTest will be cleaner. $\endgroup$
    – Mr.Wizard
    Jan 24, 2012 at 12:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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