如何将函数参数静态限制为值的子集

时间:2013-12-11 18:33:52

标签: go static-typing

如何将函数参数静态约束为所需类型的值子集?

值集将是包中定义的小集。将它作为编译时检查而不是运行时会很好。

我能够找到的唯一方法就是这样:

package foo

// subset of values
const A = foo_val(0)
const B = foo_val(1)
const C = foo_val(2)

// local interface used for constraint
type foo_iface interface {
    get_foo() foo_val
}

// type that implements the foo_iface interface
type foo_val int
func (self foo_val) get_foo() foo_val {
    return self
}

// function that requires A, B or C
func Bar(val foo_iface) {
    // do something with `val` knowing it must be A, B or C
}

现在,包的用户无法替换任何其他值来代替ABC

package main

import "foo"

func main() {
    foo.Bar(foo.A) // OK
    foo.Bar(4)     // compile-time error
}

但这似乎是很多代码来完成这个看似简单的任务。我有一种感觉,我过于复杂的事情,并错过了语言中的一些功能。

语言是否有一些功能可以用简洁的语法完成同样的事情?

2 个答案:

答案 0 :(得分:3)

Go不能这样做(我不认为,我不认为几个月让我经历过)

ADA可以,而C ++有时可以 - 但不是干净利落(constexpr和static_assert)。

但真正的问题/点在这里,为什么重要?我和Go一起使用GCC作为编译器而且GCC非常聪明,特别是对于LTO,恒定的推荐是最容易应用的优化之一,它不会打扰检查(你是(我们在C中调用的)静态在初始化AB和C时,GCC对此进行了优化(如果它有函数的定义,则使用LTO))

现在这有点偏离主题,所以我会停止使用那个混乱的blob,但测试值的理智是好的,除非你的程序是CPU绑定的,不要担心它。

总是写下更容易阅读的内容,你会感激你以后做过

所以你的运行时检查一下,如果编译器有足够的信息可以提供它,如果它可以推断(证明)它们不会抛出它们,那么它会发现它的常数值。

<强>附录

很难进行编译时检查,例如c ++中的constexpr是非常有限的(它触及的一切也必须是constexpr等) - 它与普通代码不能很好地协作。

假设一个值来自用户输入?那个检查必须是在运行时,如果你编写了两组约束(但是那些工作),那就是愚蠢的(并且违反了DRY),一个用于编译运行。

我们能做的最好的事情是让编译器真的很聪明,GCC就是。我确信其他人也很好('ces MSs one,我从来没有听到过对它的赞美,但作者很聪明,因为他们开始编写了一个c ++解析器!)

答案 1 :(得分:1)

可能符合您需求的稍微不同的方法是使函数成为类型的方法并导出有效值集,但不能构造新值。

例如:

package foo

import (
    "fmt"
)

// subset of values
const A = fooVal(0)
const B = fooVal(1)
const C = fooVal(2)

// type that implements the foo_iface interface
type fooVal int

// function that requires A, B or C
func (val fooVal) Bar() {
    fmt.Println(val)
}

使用者:

package main

import "test/foo"

func main() {
    foo.A.Bar() // OK, prints 0
    foo.B.Bar() // OK, prints 1
    foo.C.Bar() // OK, prints 2
    foo.4.Bar()     // syntax error: unexpected literal .4
    E := foo.fooVal(5) // cannot refer to unexported name foo.fooVal
}