4. 数据类型

WDL 的变量是有类型的。虽然 WDL 不像 Java 那样强制声明类型,但每个值的类型在运行时是固定的。我刚开始用 WDL 时吃过亏——以为它和 Python 一样随便赋值就行,结果被引擎报错教育了一顿。嗯,今天就把这四个基础类型掰开揉碎讲清楚。

4.1 Int:整数

Int 就是整数,支持正负。写法很直接:

Int count = 100
Int negative = -5
Int zero = 0

在 WDL 里,Int 是 64 位有符号整数。说白了就是范围±9e18够你用。我之前在项目里遇到过有人用 Int 存文件大小,结果算出来的值被截断了——因为他把浮点结果误赋给了 Int 变量。WDL 做隐式转换吗?不做。你强行把 Float 塞给 Int,引擎会报类型不匹配。

常用操作:

  • 算术运算:+ - * / %
  • 比较运算:== != < > <= >=
  • 字符串拼接(Int 会被自动转成字符串再拼)

注意除法:两个 Int 相除结果还是 Int,小数部分直接截断,不四舍五入。

Int a = 7
Int b = 3
Int c = a / b   # c = 2,而不是 2.333
小提示:如果你想保留小数,至少要把一个操作数声明为 Float。我习惯数据进门前就统一类型,省得后面调试。

4.2 Float:浮点数

Float 是双精度浮点数。写法:

Float pi = 3.14159
Float e = 2.71828
Float neg = -1.5

注意,WDL 里不能写 1.0f 或 1.0d 这种后缀。就写数字加小数点就行。整数后面不加小数点也是 Int,加了才是 Float。

有个坑:WDL 里 Float 精度够高,但做等号比较要小心。两个 Float 可能因为浮点误差不相等。我一般判断差值小于 1e-9 就算相等。

Float x = 0.1 + 0.2
Float y = 0.3
Boolean equal = (x == y)   # 很可能为 false

项目里我常用 Float 做阈值控制,比如样本质量分数、时间成本计算。记住一点:WDL 不会自动把 Int 转成 Float,需要你显式声明或用函数转换。

4.3 Boolean:布尔值

Boolean 只有两个值:true 和 false。全小写。

Boolean is_done = true
Boolean has_error = false

布尔值通常用在条件分支和循环中。WDL 支持逻辑运算符:!(非)、&&(与)、||(或)。

if (is_done && !has_error) {
  call RunReport
}

我见过有人写 if (flag == true),其实直接 if (flag) 就够。更简洁,也更符合习惯。

避坑指南:WDL 的 Boolean 不能和 Int 互换。你不能写 if (1) 表示真。只有显式的 Boolean 才能出现在条件表达式里。

4.4 String:字符串

String 用双引号包裹。支持转义符。

String name = "workflow"
String path = "/data/sample_001.fastq"
String multi = "line1\nline2"

字符串拼接用 +:

String prefix = "SRR"
String suffix = "001"
String id = prefix + suffix   # "SRR001"

还有个好用的东西:字符串插值。用 ~{} 可以把变量、表达式直接嵌入字符串中。我个人特别喜欢这个功能,比拼接省事多了。

String sample = "NA12878"
String path = "/data/{~{sample}}.bam"
# path = "/data/NA12878.bam"

这里花括号里的 ~{} 是 WDL 的占位符,不依赖外部模板引擎。你甚至可以在里面放表达式:

Int year = 2025
String tag = "v~{year}_release"
注意:字符串插值只在 WDL 的字符串内有效。如果你用 Python 的 f-string 写法 f"{var}",WDL 不认。必须用 "~{var}"。

4.5 类型对比表格

我把四个类型的关键特征整理了一下,方便对照:

类型 取值范围/精度 字面量写法 隐式转换 常见用途
Int 64位有符号 42 不会隐式转Float 计数、索引、ID
Float 双精度 3.14 不会隐式转Int 比例、阈值、时间
Boolean true/false true 不与其他类型互转 条件控制
String UTF-8 "hello" 其他类型可转String 路径、标识、信息

注意最后一行——只有 String 可以接收其他类型的字面量拼接(自动转换),其他类型之间没有隐式转换。这也是我踩过的坑:想给一个 Float 变量赋整数值,直接写 3 是 Int 类型,赋值失败。得写 3.0 才行。

4.6 实际代码片段

下面是一个综合示例,用到了所有四种类型:

workflow TypeDemo {
  input {
    String sample_id = "sample_01"
    Int read_count = 100000
    Float quality_threshold = 20.5
    Boolean enable_qc = true
  }

  command {
    echo "Processing ~{sample_id} with ~{read_count} reads"
  }
}

这里我刻意把变量和默认值绑在一起写。默认值是一个好习惯——即使输入端没给值,也能跑。不过注意默认值的类型必须和声明的类型一致。如果你写 Int read_count = "100000" ,引擎会报类型错误。

再来个小技巧:你可以用 defined() 函数检查变量是否被赋值,这在处理可选输入时特别有用。例如:
if (defined(quality_threshold)) {
  # 做质量控制
}

4.7 本章知识结构图

下面这张图梳理了四种基础类型的关系和转换规则。画布用淡蓝色,节点用不同的颜色区分类型,箭头表示允许的自动转换方向。

WDL 基本数据类型与转换规则 Int Float Boolean String 强制转换 字符串拼接自动转换 自动转换 规则说明 • 无隐式类型转换 • 仅字符串拼接时 自动转字符串 • 其他需显式转换 虚线箭头表示强制转换(需要用户手动操作) 实线箭头表示自动转换(仅字符串拼接情境)

4.8 总结两句