Built-in Operators

Unary and binary operators in WDL

WDL provides the standard unary and binary mathematical and logical operators. The following tables list the valid operand and result type combinations for each operator. Using an operator with unsupported types results in an error.

In operations on mismatched numeric types (e.g., Int + Float), the Int is first coerced to Float, and the result type is Float. This may result in loss of precision, for example if the Int is too large to be represented exactly by a Float. A Float can be converted to Int with the ceil, round, or floor functions.

§Unary Operators

OperatorRHS TypeResult
-FloatFloat
-IntInt
!BooleanBoolean

§Binary Operators on Primitive Types

LHS TypeOperatorRHS TypeResultSemantics
Boolean==BooleanBoolean
Boolean!=BooleanBoolean
Boolean||BooleanBoolean
Boolean&&BooleanBoolean
Deprecated Boolean>BooleanBooleantrue is greater than false
Deprecated Boolean>=BooleanBooleantrue is greater than false
Deprecated Boolean<BooleanBooleantrue is greater than false
Deprecated Boolean<=BooleanBooleantrue is greater than false
Int+IntInt
Int-IntInt
Int*IntInt
Int/IntIntInteger division
Int%IntIntInteger division, return remainder
Int==IntBoolean
Int!=IntBoolean
Int>IntBoolean
Int>=IntBoolean
Int<IntBoolean
Int<=IntBoolean
Deprecated Int+StringString
Int+FloatFloat
Int-FloatFloat
Int*FloatFloat
Int/FloatFloat
Int==FloatBoolean
Int!=FloatBoolean
Int>FloatBoolean
Int>=FloatBoolean
Int<FloatBoolean
Int<=FloatBoolean
Float+FloatFloat
Float-FloatFloat
Float*FloatFloat
Float/FloatFloat
Float%FloatFloat
Float==FloatBoolean
Float!=FloatBoolean
Float>FloatBoolean
Float>=FloatBoolean
Float<FloatBoolean
Float<=FloatBoolean
Deprecated Float+StringString
Float+IntFloat
Float-IntFloat
Float*IntFloat
Float/IntFloat
Float%IntFloat
Float==IntBoolean
Float!=IntBoolean
Float>IntBoolean
Float>=IntBoolean
Float<IntBoolean
Float<=IntBoolean
String+StringStringConcatenation
String+FileFile
String==StringBooleanUnicode comparison
String!=StringBooleanUnicode comparison
String>StringBooleanUnicode comparison
String>=StringBooleanUnicode comparison
String<StringBooleanUnicode comparison
String<=StringBooleanUnicode comparison
Deprecated String+IntString
Deprecated String+FloatString
File==FileBoolean
File!=FileBoolean
File==StringBoolean
File!=StringBoolean
Deprecated File+FileFileappend file paths - error if second path is not relative
Deprecated File+StringFileappend file paths - error if second path is not relative

Boolean operator evaluation is minimal (or "short-circuiting"), meaning that:

  1. For A && B, if A evalutes to false then B is not evaluated
  2. For A || B, if A evaluates to true then B is not evaluated.

WDL Strings are compared by the unicode values of their corresponding characters. Character a is less than character b if it has a lower unicode value.

Except for String + File, all concatenations between String and non-String types are deprecated and will be removed in WDL 2.0. The same effect can be achieved using string interpolation.

§Equality of Compound Types

LHS TypeOperatorRHS TypeResult
Array==ArrayBoolean
Array!=ArrayBoolean
Map==MapBoolean
Map!=MapBoolean
Pair==PairBoolean
Pair!=PairBoolean
Struct==StructBoolean
Struct!=StructBoolean
Object==ObjectBoolean
Object!=ObjectBoolean

In general, two compound values are equal if-and-only-if all of the following are true:

  1. They are of the same type.
  2. They are the same length.
  3. All of their contained elements are equal.

Since Arrays and Maps are ordered, the order of their elements are also compared. For example:

Example: array_map_equality.wdl

version 1.1

workflow array_map_equality {
  output {
    # arrays and maps with the same elements in the same order are equal
    Boolean is_true1 = [1, 2, 3] == [1, 2, 3]
    Boolean is_true2 = {"a": 1, "b": 2} == {"a": 1, "b": 2}

    # arrays and maps with the same elements in different orders are not equal
    Boolean is_false1 = [1, 2, 3] == [2, 1, 3]
    Boolean is_false2 = {"a": 1, "b": 2} == {"b": 2, "a": 1}
  }
}

Example input:

{}

Example output:

{
  "array_map_equality.is_true1": true,
  "array_map_equality.is_true2": true,
  "array_map_equality.is_false1": false,
  "array_map_equality.is_false2": false
}

Type coercion can be employed to compare values of different but compatible types.

Example: compare_coerced.wdl

version 1.1

workflow compare_coerced {
  Array[Int] i = [1, 2, 3]
  Array[Float] f1 = i
  Array[Float] f2 = [1.0, 2.0, 3.0]

  output {
    # Ints are automatically coerced to Floats for comparison
    Boolean is_true = f1 == f2
  }
}

Example input:

{}

Example output:

{
  "compare_coerced.is_true": true
}

§Equality and Inequality Comparison of Optional Types

The equality and inequality operators are exceptions to the general rules on coercion of optional types. Either or both operands of an equality or inequality comparison can be optional, considering that None is equal to itself but no other value.

Example: compare_optionals.wdl

version 1.1

workflow compare_optionals {
  Int i = 1
  Int? j = 1
  Int? k = None

  output {
    # equal values of the same type are equal even if one is optional
    Boolean is_true1 = i == j
    # k is undefined (None), and so is only equal to None
    Boolean is_true2 = k == None
    # these comparisons are valid and evaluate to false
    Boolean is_false1 = i == k
    Boolean is_false2 = j == k
  }
}

Example input:

{}

Example output:

{
  "compare_optionals.is_true1": true,
  "compare_optionals.is_true2": true,
  "compare_optionals.is_false1": false,
  "compare_optionals.is_false2": false
}