Skip to content

Commit 6bc8f2b

Browse files
claymcleodjdidion
andcommitted
feat: adds enumerations
This commit adds enumerations to WDL. Enumerations in WDL are valued, meaning that they have an assigned valued for each variant therein. These values can either be explicitly or implicitly typed. Ultimately, enumerations are aimed at improved UX regarding a limited set of valid values within a particular context. Relevant issues: #139, #658 Co-authored-by: jdidion <github@didion.net>
1 parent 043a2ab commit 6bc8f2b

File tree

1 file changed

+172
-1
lines changed

1 file changed

+172
-1
lines changed

SPEC.md

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Revisions to this specification are made periodically in order to correct errors
3939
- [Map\[P, Y\]](#mapp-y)
4040
- [🗑 Object](#-object)
4141
- [Custom Types (Structs)](#custom-types-structs)
42+
- [Enumeration Types (Enums)](#enumeration-types-enums)
4243
- [Hidden and Scoped Types](#hidden-and-scoped-types)
4344
- [`Union` (Hidden Type)](#union-hidden-type)
4445
- [`hints`, `input`, and `output` (Scoped Types)](#hints-input-and-output-scoped-types)
@@ -73,9 +74,12 @@ Revisions to this specification are made periodically in order to correct errors
7374
- [WDL Documents](#wdl-documents)
7475
- [Versioning](#versioning)
7576
- [Struct Definition](#struct-definition)
77+
- [Enum Definition](#enum-definition)
78+
- [Enum Usage](#enum-usage)
7679
- [Import Statements](#import-statements)
7780
- [Import URIs](#import-uris)
7881
- [Importing and Aliasing Structs](#importing-and-aliasing-structs)
82+
- [Importing and Aliasing Enums](#importing-and-aliasing-enums)
7983
- [Task Definition](#task-definition)
8084
- [Task Inputs](#task-inputs)
8185
- [Task Input Localization](#task-input-localization)
@@ -1357,7 +1361,7 @@ Due to the lack of explicitness in the typing of `Object` being at odds with the
13571361

13581362
##### Custom Types (Structs)
13591363

1360-
WDL provides the ability to define custom compound types called [structs](#struct-definition). `Struct` types are defined directly in the WDL document and are usable like any other type. A struct is defined using the `struct` keyword, followed by a unique name, followed by member declarations within braces. A struct definition contains any number of declarations of any types, including other `Struct`s.
1364+
WDL provides the ability to define custom compound types called [structs](#struct-definition). `Struct` types are defined at the top-level of the WDL document and are usable like any other type. A struct is defined using the `struct` keyword, followed by a unique name, followed by member declarations within braces. A struct definition contains any number of declarations of any types, including other `Struct`s.
13611365

13621366
A declaration with a custom type can be initialized with a struct literal, which begins with the `Struct` type name followed by a comma-separated list of name-value pairs in braces (`{}`), where name-value pairs are delimited by `:`. The member names in a struct literal are not quoted. A struct literal must provide values for all of the struct's non-optional members, and may provide values for any of the optional members. The members of a struct literal are validated against the struct's definition at the time of creation. Members do not need to be in any specific order. Once a struct literal is created, it is immutable like any other WDL value.
13631367

@@ -1485,6 +1489,47 @@ Example output:
14851489

14861490
Note that the ability to assign values to `Struct` declarations other than struct literals is deprecated and will be removed in WDL 2.0.
14871491

1492+
##### Enumeration Types (Enums)
1493+
1494+
An enumeration (or "enum") is a closed set of enumerated values (known as "variants") that are considered semantically valid in a specific context. An enum is defined at the top-level of the WDL document and can be used as a declaration type anywhere in the document.
1495+
1496+
An enum is defined using the `enum` keyword, followed by a globally unique name, followed by a comma-delimited list of identifiers—optionally tagged with values—in braces. When referring to a variant within an enum, for example, when assigning to an enum declaration, the `<name>.<variant>` syntax should be used.
1497+
1498+
```wdl
1499+
enum FileKind {
1500+
FASTQ,
1501+
BAM
1502+
}
1503+
1504+
task process_file {
1505+
input {
1506+
File infile
1507+
FileKind kind = FileKind.FASTQ
1508+
}
1509+
1510+
command <<<
1511+
echo "Processing ~{kind} file"
1512+
...
1513+
>>>
1514+
}
1515+
workflow process_files {
1516+
input {
1517+
Array[File] files
1518+
FileKind kind
1519+
}
1520+
1521+
scatter (file in files) {
1522+
call process_file {
1523+
input:
1524+
infile = file,
1525+
kind = kind
1526+
}
1527+
}
1528+
}
1529+
```
1530+
1531+
As an example, consider a workflow that processes different types of NGS files and has a `file_kind` input parameter that is expected to be either "FASTQ" or "BAM". Using `String` as the type of `file_kind` is not ideal - if the user specifies an invalid value, the error will not be caught until runtime, perhaps after the workflow has already run for several hours. Alternatively, using an `enum` type for `file_kind` restricts the allowed values such that the execution engine can validate the input prior to executing the workflow.
1532+
14881533
#### Hidden and Scoped Types
14891534

14901535
A hidden type is one that may only be instantiated by the execution engine, and cannot be used in a declaration within a WDL file.
@@ -3352,6 +3397,115 @@ struct Invalid {
33523397
}
33533398
```
33543399

3400+
## Enum Definition
3401+
3402+
An `Enum` is an enumerated type. Enums enable the creation of types that represent closed sets of enumerated values (called "variants") that are semantically valid in a specific context. Once defined, an `Enum` type can be used as the type of a declaration like any other type. However, new variants of an `Enum` cannot be created. Instead, a declaration having an `Enum` type must be assigned one of the variants created as part of the `Enum`'s definition.
3403+
3404+
An enum definition is a top-level WDL element, meaning it is defined at the same level as tasks, workflows, and structs, and it cannot be defined within a task or workflow body. An enum is defined using the `enum` keyword, followed by a name that is unique within the WDL document, and a body containing a comma-delimited list of variants (and optional assigned values) in braces (`{}`).
3405+
3406+
```wdl
3407+
enum Color {
3408+
Red,
3409+
Orange,
3410+
Yellow,
3411+
Green,
3412+
Blue,
3413+
Purple
3414+
}
3415+
```
3416+
3417+
Enums are valued, meaning that each variant within an enum has an associated value. To indicate the type of the assigned value for each variant, enums can either be _explicitly_ or _implicitly_ typed.
3418+
3419+
* Explicitly typed enums take an explicit type assignment within square brackets after the enum's identifier that declares the type of the value. Where possible, explicitly typed enums should attempt to resolve mismatched types via coercion.
3420+
* Implicitly typed enums are enums where each assigned value can be unambiguously mapped to a single [primitive type](#primitive-types) and, thus, does not need the type to be specified in brackets. Enums that are implicitly typed and for which no values are assigned are assumed to be `String`-valued where each value is a `String` that matches the variant name. Importantly, all assigned values within an implicitly typed enum must have the same type or an error is thrown.
3421+
3422+
```wdl
3423+
# An explicitly typed enum that is `Float`-valued.
3424+
enum FruitCosts[Float] {
3425+
Banana = 1.33,
3426+
Orange = 2.20,
3427+
Apple = 3.47
3428+
}
3429+
3430+
# An explicitly typed enum that is `Float`-valued. Because the enum is
3431+
# explicitly typed, the `ThreePointOh` variant can be coerced to a `Float`,
3432+
# which is a valid enumeration definition.
3433+
enum FavoriteFloat[Float] {
3434+
ThreePointOh = 3,
3435+
FourPointOh = 4.0
3436+
}
3437+
3438+
# ERROR: because the enum is implicitly typed, the type cannot be unambiguously
3439+
# resolved, which results in an error.
3440+
enum FavoriteNumber {
3441+
ThreePointOh = 3,
3442+
FourPointOh = 4.0
3443+
}
3444+
3445+
# An implicitly typed enum that is `String`-valued.
3446+
enum Whitespace {
3447+
Tab = "\t",
3448+
Space = " "
3449+
}
3450+
3451+
# An implicitly typed enum that is implied to be `String`-valued with the
3452+
# values "FASTQ" and "BAM" respectively.
3453+
enum FileKind {
3454+
FASTQ,
3455+
BAM
3456+
}
3457+
```
3458+
3459+
### Enum Usage
3460+
3461+
An `Enum`'s variants are [accessed](#member-access) using a `.` to separate the variant name from the `Enum`'s identifier.
3462+
3463+
A declaration with an `Enum` type can only be initialized by referencing a variant directly or by assigning it to the value of another declaration of the same `Enum` type.
3464+
3465+
If two enum values are of the same type, they can be tested for equality (i.e., using `==` or `!=`) by testing the equality of the inner values assigned to each variant. Similarly, if two enum values are of the same type, they can be compared (i.e., using `>`, `>=`, `<`, `<=`) by performing the analogous operations on their assigned values. If two enums are _not_ of the same type, they cannot be compared and should throw a type mismatch error. To that end, an `Enum` cannot be coerced to or from any other type.
3466+
3467+
```wdl
3468+
version 1.2
3469+
3470+
enum Pet {
3471+
Cat,
3472+
Dog,
3473+
Rat
3474+
}
3475+
3476+
enum HotFood {
3477+
Dog,
3478+
Potato,
3479+
Tamale
3480+
}
3481+
3482+
task pet_pet {
3483+
input {
3484+
Pet? pet
3485+
}
3486+
3487+
Pet my_pet = select_first([pet, Pet.Dog])
3488+
3489+
Array[String] all_pet_names = [
3490+
Pet.Cat,
3491+
Pet.Dog,
3492+
Pet.Rat
3493+
]
3494+
3495+
command <<<
3496+
echo "There are ~{length(all_pet_names)} kinds of pet: ~{sep(", ", all_pet_names)}"
3497+
echo "I have a pet ~{my_pet}"
3498+
>>>
3499+
}
3500+
3501+
task error {
3502+
output {
3503+
# ERROR: these two types cannot be compared.
3504+
Boolean comparison = Pet.Dog == HotFood.Dog
3505+
}
3506+
}
3507+
```
3508+
33553509
## Import Statements
33563510

33573511
Although a WDL workflow and the task(s) it calls may be defined completely within a single WDL document, splitting it into multiple documents can be beneficial in terms of modularity and code resuse. Furthermore, complex workflows that consist of multiple subworkflows must be defined in multiple documents because each document is only allowed to contain at most one workflow.
@@ -3542,6 +3696,23 @@ struct Patient {
35423696
}
35433697
```
35443698

3699+
### Importing and Aliasing Enums
3700+
3701+
`Enum`s are [imported in the same way as `Struct`s](#struct-namespacing) and have the same namespacing rules, namely that `Enum`s exist in the document's global scope, and importing an `Enum` copies its definition into the global scope of the importing document (potentially using an alias).
3702+
3703+
```wdl
3704+
version 1.2
3705+
3706+
import "color.wdl" alias Color as Hue
3707+
3708+
workflow another_wf {
3709+
input {
3710+
Hue hue = Hue.Blue
3711+
}
3712+
...
3713+
}
3714+
```
3715+
35453716
## Task Definition
35463717

35473718
A WDL task can be thought of as a template for running a set of commands - specifically, a Bash script - in a manner that is (ideally) independent of the execution engine and the runtime environment.

0 commit comments

Comments
 (0)