如何表达“切片(字符串或’其他切片’)的类型”

huangapple go评论73阅读模式
英文:

How to express type of "slice of (string or 'other such slice')"

问题

如何在Go中表示“一个由字符串或其他类似列表组成的列表”类型?基本上是经典的“树表示为无限嵌套的列表和一些值(在这个例子中是字符串)”。

我正在寻找最简单的S表达式表示方式(它本身将是AST的最简单表示方式),在Python中看起来像这样:

sexp1 := []interface{}{"+", "x", "y", []interface{}{"*", "10", "myVal"}}
sexp2 := []interface{}{"foo", "bar", "baz"}
sexp3 := []interface{}{[][][]interface{}{{[]interface{}{"gooo"}, "moo"}}, "too", []interface{}{"yoo", "2"}}

在Go中,所有这些表达式的类型将是什么?显然,[][]string不起作用,因为这样做不起作用:

func makeSexp(parserName string, values ...[][]string) [][]string {
    return append([]string{parserName}, values...)
}

(编译错误:1. cannot use values (type [][][]string) as type []string in append,2. cannot use append([]string literal, values...) (type []string) as type [][]string in return argument。)

...而完全不带类型的版本可以工作(但我不想完全放弃类型安全!):

func makeSexp(parserName string, values ...interface{}) interface{} {
    return append([]interface{}{parserName}, values...)
}
英文:

How can I express in Go a type which is "a list of (strings or other such lists)"? Basically the good ol' "tree represented as infinitely nested lists of lists and something as values (strings in this example)"

I'm looking for the simplest possible representation of an S-expression (which itself would be the simplest of an AST), which in Python would look like this:

sexp1 = ["+", "x", "y", ["*", "10", "myVal"]]
sexp2 = ["foo" "bar" "baz"]
sexp3 = [ [ [["gooo"], "moo"] ], "too", ["yoo", "2"] ]

What type would all these expressions have in Go? Obviously [][]string doesn't work, as this doesn't work:

func makeSexp(parserName string, values ...[][]string) [][]string {
	return append([]string{parserName}, values...)
}

(Compile errors: 1. cannot use values (type [][][]string) as type []string in append, 2. cannot use append([]string literal, values...) (type []string) as type [][]string in return argument.)

...while the fully untyped version works (but I don't want to completely give up type safety!):

func makeSexp(parserName string, values ...interface{}) interface{} {
	return append([]interface{}{parserName}, values...)
}

答案1

得分: 5

很遗憾,Go语言不支持代数数据类型,所以为了使其类型安全,你最好创建一个未导出的接口,并创建两个实现:

type sExp interface {
    sExp()
}

type s string

func (s) sExp() {}

type l []sExp

func (l) sExp() {}

// ...
var sexp1 sExp = l{s("+"), s("1"), s("2"), l{s("*"), s("10"), s("myVal")}}

这基本上是Protobuf编译器处理oneof情况的方式。虽然仍然需要大量的类型切换或类型断言来处理,但至少你可以确保模块外部的代码无法对其进行修改。

Playground: https://play.golang.org/p/KOvFqJEvxZ

英文:

Unfortunately, Go doesn't support algebraic data types, so your best bet to make it type-safe is to create an unexported interface, and make two implementations of it:

type sExp interface {
	sExp()
}

type s string

func (s) sExp() {}

type l []sExp

func (l) sExp() {}

// ...
var sexp1 sExp = l{s("+"), s("1"), s("2"), l{s("*"), s("10"), s("myVal")}}

This is basically how Protobuf compilers deal with e.g. oneof cases. This will still need a lot of type switches or type assertions to work with, but at least you can be sure that nothing outside of your module will be able to tinker with it.

Playground: https://play.golang.org/p/KOvFqJEvxZ.

huangapple
  • 本文由 发表于 2017年9月2日 18:59:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/46013074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定