2. 值、类型与函数

  • 第二章,共二十章
  • 阅读约 3 分钟

let 绑定不可变,var 可变。基本类型有 Int、Float、Bool、String。 顶层函数必须写全参数类型与返回类型——签名即契约。

fn square(x: Int) -> Int = x * x

fn abs(x: Int) -> Int =
  if x < 0 { 0 - x } else { x }

pub fn main() -> Unit !io = {
  var total = 0
  total = total + square(3)
  total = total + abs(-4)
  println(to_string(total))
}
13

点调用:x.f(a) 就是 f(x, a)

函数主要作用于它的第一个参数时,就把这个参数写在前面。x.f(a) 与 f(x, a) 完全等价: 接收者填第一个形参,括号里是其余实参。点调用连成链,读起来是数据的流向;嵌套调用则要由内向外读:

fn double(x: Int) -> Int = x * 2
fn inc(x: Int) -> Int = x + 1

pub fn main() -> Unit !io = {
  let n = 5
  n.double().inc().to_string().println()
  let total = [1, 2, 3, 4]
    .filter(x => x > 1)
    .map(double)
    .fold(0, (acc, x) => acc + x)
  println(total.to_string())
}
11
18

对同一个值的两步及以上操作一律写成链,不写嵌套调用:写 e.insert(5).insert(2), 不写 insert(insert(e, 5), 2)。链太长就一步一行,续行以 . 开头。没有天然主语的调用保持普通写法: 构造器(Node(l, v, r))、range(0, n)、println("hi"),以及 max(a, b) 这类对称函数。

点调用只找得到以裸名在作用域内的函数:你自己的函数、prelude 的 builtin、选择性引入的名字。 只写 use std/str 时,s.split(",") 是错误,报错会提示写 str.split(s, ","), 或加上 use std/str.{split}。完整的解析顺序见 spec §4.3。

管道 |>

管道是同一个调用的另一种拼写:x |> f(a) 就是 f(x, a)。点写法不合适时用它:数据起点是字面量或较大的表达式、 最后一步是汇或 lambda、函数带模块限定而你不想引入它的裸名(s |> str.split(",")), 或者右侧是函数值而不是调用。它的优先级比其余运算符都低,所以 a + b |> f 是 f(a + b)。 一条链要么全用点,要么全用管道,不混写。

fn double(x: Int) -> Int = x * 2
fn inc(x: Int) -> Int = x + 1

pub fn main() -> Unit !io =
  5 |> double |> inc |> to_string |> println
11

具名实参与默认值

形参可以带默认值,写作 name: Type = expr,调用时就可以省掉这个实参。任何实参也都可以 按名字传:位置实参从左往右占位,具名实参占它点名的那一格,名字之间的先后随意。

fn greet(name: String, greeting: String = "Hello", punct: String = "!") -> String =
  "${greeting}, ${name}${punct}"

pub fn main() -> Unit !io = {
  println(greet("Dawn"))
  println(greet("Dawn", punct: "?"))
  println(greet(punct: ".", name: "reader", greeting: "Welcome"))
}
Hello, Dawn!
Hello, Dawn?
Welcome, reader.

默认值在每次省掉它的调用里重新求值,而且必须是纯的:要是它能做 io,一次调用做不做 io 就取决于调用方传没传这个实参。实参按书写顺序求值,不管它们落在哪一格。名字属于签名, 所以函数值没有名字:let g = greet 之后,g(name: "x") 是错误,g 要求三个实参一个 不少。构造器也一样收名字,字段名就是它的形参名(Rect(w: 3.0, h: 4.0))。

另见:spec.md §2.1、§3.1、§4.3、§4.4