Converting slices of stringer to slices of strings

2,634 views
Skip to first unread message

Elazar Leibovich

unread,
Dec 30, 2011, 8:59:45 AM12/30/11
to golang-nuts
When pretty printing an AST I have types of this form

type Func struct {
Variables []Var
ReturnVal Type
}

type Type struct {
Name string
}

type Var struct {
Names []Ident
Typ Type
}

type Ident string

and when pretty printing it, I want to write code of this form

func (f *Func) String() string{
return "func "+f.Name+"("+strings.Join(f.Variables.map( x =>
x.String())+")"
}

func (v *Var) String() string {
return "var "+strings.Join(v.Names,",")+" "+v.Typ.String()
}

Of course you can't write that with go, and I wonder what's the "best
practice" in Go for this cases.

tux21b

unread,
Dec 30, 2011, 9:59:28 AM12/30/11
to golan...@googlegroups.com
I would write my own join() method which either accepts fmt.Stringer's  (or even interface{}). Maybe something like this?


-christoph

Jim Teeuwen

unread,
Dec 30, 2011, 1:47:26 PM12/30/11
to golan...@googlegroups.com
To take advantage of the presence of String() on whatever you pass into join(), a little tweak is in order.

Rémy Oudompheng

unread,
Dec 30, 2011, 2:51:38 PM12/30/11
to golan...@googlegroups.com
It should also be possible to use text/template for that, though it
may look like overkill.

Rémy.

Elazar Leibovich

unread,
Dec 31, 2011, 11:42:20 AM12/31/11
to golang-nuts
Both tux21b and Jim's solution don't make sense to me.

I'm having a []Var. The type Var is a Stringer.

I need to get []string, by applying elt.String() to each element in
the slice.

Since arrays are not covariant, I can't define a function getting a
slice of Stringers ([]Stringer), this function will not accept []Var,
even though it is essentially a slice of stringers.

How does your code help? Your function don't even get a slice of
anything, it's just a vararg function.

tux21b

unread,
Dec 31, 2011, 12:16:21 PM12/31/11
to golan...@googlegroups.com
Hi Elazar,

why do you require a []string slice? In the example you have posted, you just
need the slice to join it (using strings.Join). Writing a join-anything function is
a bit simpler and more efficient than converting anything to a string-slice just
to join it again.

Anyway, here is exactly what you have asked for (if I am not mistaken), but I
wouldn't consider this a best practice in this case.


Note: Instead of the Str type, you can use any type which has a String()
method (e.g. your Func, Type or Var type). The parameter args can
be any slice of fmt.Stringers.

-christoph


Elazar Leibovich

unread,
Dec 31, 2011, 12:50:24 PM12/31/11
to golang-nuts
I feared that using reflections will be too slow, but I ended up using
reflection anyhow http://play.golang.org/p/fh1MO0igIl

The gist of it

func mapstringer(xs interface{}) []string {
slice := reflect.ValueOf(xs)
retval := make([]string,slice.Len())
for i := 0; i < slice.Len(); i++ {
elt,ok := slice.Index(i).Interface().(fmt.Stringer)
if ! ok {panic("got slice with non Stringer elements")}
retval[i] = elt.String()
}
return retval
}

If there's a better go-ish way to achieve similar effect I'll be glad
hear (or is it a good style of Go code? runtime reflection as ad hoc
generics...)

Jan Mercl

unread,
Dec 31, 2011, 1:11:51 PM12/31/11
to golan...@googlegroups.com
On Saturday, December 31, 2011 6:50:24 PM UTC+1, Elazar Leibovich wrote:
I feared that using reflections will be too slow, but I ended up using
reflection anyhow http://play.golang.org/p/fh1MO0igIl

The gist of it

func mapstringer(xs interface{}) []string {
        slice := reflect.ValueOf(xs)
        retval := make([]string,slice.Len())
        for i := 0; i < slice.Len(); i++ {
                elt,ok := slice.Index(i).Interface().(fmt.Stringer)
                if ! ok {panic("got slice with non Stringer elements")}
                retval[i] = elt.String()
        }
        return retval
}

If there's a better go-ish way to achieve similar effect I'll be glad
hear (or is it a good style of Go code? runtime reflection as ad hoc
generics...)

I would not even think about writing an utility function for this neither about using reflection. Instead I would probably write something like a ~3 liner range statement in the place(s) where needed.

Rob 'Commander' Pike

unread,
Dec 31, 2011, 1:37:52 PM12/31/11
to golan...@googlegroups.com
Print will already do most of the work given a slice of interface{}, which you sort of have in the middle there, and could have more directly if you tried. So although I agree what you're doing is ill-informed, be aware Go can help you more than you're letting it.

package main
import "fmt"

type X int
func (x X) String() string { return fmt.Sprint("X", int(x)) }

func main() {
slice := []interface{}{ X(1), X(2), X(3) }
s := fmt.Sprint(slice)
s = s[1:len(s)-1] // why? an exercise for the reader.
fmt.Println(s)
}

output:

X1 X2 X3

-rob

Jan Mercl

unread,
Dec 31, 2011, 1:50:00 PM12/31/11
to golan...@googlegroups.com
On Saturday, December 31, 2011 7:37:52 PM UTC+1, r wrote:

        s = s[1:len(s)-1] // why? an exercise for the reader.

Brackets! ;-) 

John Asmuth

unread,
Dec 31, 2011, 2:12:18 PM12/31/11
to golan...@googlegroups.com
You're one of those guys who fills in the crossword in the library's copy of the newspaper, aren't you?! 

Jan Mercl

unread,
Dec 31, 2011, 2:23:04 PM12/31/11
to golan...@googlegroups.com
On Saturday, December 31, 2011 8:12:18 PM UTC+1, John Asmuth wrote:
You're one of those guys who fills in the crossword in the library's copy of the newspaper, aren't you?! 

Nice idea, maybe I should give it a try. Happy New Year 2U2 o<(78-D)

Elazar Leibovich

unread,
Dec 31, 2011, 11:44:47 PM12/31/11
to golang-nuts
The problem is, I'm not always joining with commas.

Can you please explain me the problem with my approach? Please see my
original post for extra details.

What I'm trying to do, is, given an AST, I want to pretty print it.
The AST is built roughly like the one at go/ast library, and contains
things like "type Stmts struct {Statmements []Stmt ...}". Each AST
elements knows how to pretty prints itself, by combining and joining
the pretty-printed output of its elements.

So for example, Stmts will Join on "\n" all the outputs of
Stmts.Statements.

What's the Go-ish way to achieve that?

Elazar Leibovich

unread,
Dec 31, 2011, 11:47:34 PM12/31/11
to golang-nuts
Join is fine, but your original example didn't get any slice of
Stringers, but a variable number of argument of interfaces. I ended up
indeed with a similar reflection-based approach.

DisposaBoy

unread,
Jan 1, 2012, 3:02:26 AM1/1/12
to golan...@googlegroups.com
Given a slice `s` and a variadic function `f` e.g. `func(sep string, ...args interface{})`, you can call it in 2 ways:

    f(",", a, b, c)

or

    f(",", s...)

I didn't write the original example so don't take my word for it but I assume that's why it takes a vararg instead of explicitly a slice,
that way if you want to join a couple variables you don't have to go and construct a slice.
Reply all
Reply to author
Forward
0 new messages