The right conditional

Q has two forms of conditional evaluation: Cond ($) and if. The following rules help you write code where useful information is conveyed by your choice of conditional.

Reference: Cond, if

if has the syntax:

if[cond;
  true1;
  …
  trueN]

$[] has the syntax:

r:$[cond1; true1;
  …
  condN; trueN;
  false]

Cond returns a result; if does not. (Strictly: it returns a null.) Use if only when you do not want to capture a result. Another way to say this is:

Use if to govern a side effect.

foo:{
  if[x<0;log.msg"Negative value: ",string x];
  …
}

Assigning a default value to a function argument can be thought of as a side effect.

foo:{
  if[x~::;x:101];
  …
  }

This distinction is not always clear. The example above could have been written:

x:$[x~::;101;x];

Here, the thought is: there will be a value for x, whether supplied or not. The same can be expressed by indexing.

x:(x;101)x~::;

If in doubt, avoid if.

Validation

It is common for a function to validate its arguments before doing its work. This can be written as a multiply-nested Cond.

foo:{
  $[x<0;[log.error"Negative value: ",string x;0];
    y>100*x;[log.error"Excess y: ",string y;0];
    …
    ]
  }

If there is work to do between the tests, if may be more legible.

foo:{
  if[x<0;log.error"Negative value: ",string x;:0];
  s:bar x;
  if[y>100*s;log.error"Excess y: ",string y;:0];
  …
  }

Signalling an error is a side effect. Use if.

foo:{
  if[x=0;'"Cannot be zero"];
  …
  }

If-then-else

Although if does not support if-then-else logic, it should be used even when that logic is required but where side effects are intended.

if[b:x>5;foo::x];
if[not b;goo::y];

Indexing

If/else and case constructions can often be represented as indexes. Indexing has great power of expression.

Consider the example above, setting either foo or goo. It could also be written as

(`goo`foo x>5)set x

Suppose instead that foo and goo are lamdbdas that produce quite different side effects. We would use if to make it clear no result is being captured.

if[b:x>5;foo x];
if[not b;goo x];

But if foo and goo returned results we could write

r:$[x>5;foo x;goo x]

or even

r:(goo;foo)[x>5] x

Switch statement

A switch statement produces different outcomes according to the value of a variable. It is easy to represent with Cond.

foo:{[age]
  "You are ",$[x=1;"one";
               x=2;"two";
               x=3;"three";
               x=4;"four";
               "not one, two, three, or four"] }

Repeated tests of the same value suggests an alternative. We can use Find and two lists.

foo:{"You are ",("one";"two";"three";"four";"not one, two, three, or four")1 2 3 4?x}

The example above could be described as an “n+1 dictionary”: it specifies a default result for when x is not a key.

If the range of x is known, a dictionary can be used. Suppose foo, goo, hoo, and joo are functions to be executed on y according to the word in x, and x will be one of "append", "insert", "replace", or "execute".

d:`append`insert`replace`execute!(foo;goo;hoo;joo)
d[`$x]@y

Consistent use of if and the conditional will make your code more readable:

  • seeing if you know that a side effect is sought and a result is not;
  • seeing $[], you know that a result is intended unconditionally.

Reference: do, while, Do, While, Vector Conditional
Basics: Controlling evaluation