4.2. 函数值

在凹语言中,函数可以被当作一种特殊的值,例如:

// 版权 @2023 凹语言 作者。保留所有权利。

func Inc(i: i32) => i32 { return i + 1 }
func Dec(i: i32) => i32 { return i - 1 }

func main {
    f := Inc
    println(f(42)) // 43

    f = Dec
    println(f(42)) // 41
}

上例中,f 即为函数值,函数值可以被调用,调用方法与函数调用无异。

函数的类型由其参数以及返回值类型决定,通常这些信息被称为函数签名(Signature),如果两个函数 A 和 B 拥有相同签名,意味着它们:

  • 参数个数相同;
  • 返回值个数相同;
  • 对于任意 n,函数 A 的第 n 个参数的类型与 B 的第 n 个参数类型相同;
  • 对于任意 m,函数 A 的第 m 个返回值的类型与 B 的第 m 个返回值类型相同。

函数值的类型也是通过函数签名定义的,比如上例中函数值 f 的类型为 func(i32) => i32,因此上例中 f 的快捷声明 f := Inc 等价于:

    f: func(i32) => i32 // f == nil
    f = Inc

与其他类型的值一样,函数值也为0值初始化,对应值为 nil

在凹语言中,类型不同的值不能相互赋值,这一点对函数值同样有效,由于函数类型由签名确定,因此将一个函数赋值给签名不同的函数值被视为非法,例如:

func Inc(i: i32) => i32 { return i + 1 }

func main {
    f: func(i32)
    f = Inc // 编译错误
}

既然被称为“值”,意味着函数值可以作为参数、和返回值在不同函数间传递,例如:

// 版权 @2023 凹语言 作者。保留所有权利。

func inc(i: i32) => i32 { return i + 1 }
func dec(i: i32) => i32 { return i - 1 }
func getFunc(opCode: i32) => func(i32) => i32 {
    if opCode == 0 {
        return inc
    } else if opCode == 1 {
        return dec
    } else {
        return nil
    }
}

func useFunc(i: i32, f: func(i32) => i32) {
    if f == nil {
        println("f == nil")
        return
    }
    println(f(i))
}

func main {
    useFunc(42, getFunc(0)) // 43
    useFunc(42, getFunc(1)) // 41
    useFunc(42, getFunc(2)) // f == nil
    getFunc(2)(42)          // 运行时异常
}

与其他基本类型不同,函数值只能与 nil 比较,既:函数值位于操作符 ==!= 左侧时,右侧只能为 nil,对两个非常量函数值执行比较操作被视为非法。

如果被调用的函数值为 nil,将触发不可恢复的运行时异常。

函数值与 C 系语言中的函数指针作用类似,可以更灵活的动态调整执行分支。但需要指出的时,相比于直接调用函数,调用函数值有一些额外消耗,性能敏感的场合需要格外注意。