Re: [go-nuts] Function to stringify an array of anything

738 views
Skip to first unread message

chris dollin

unread,
Mar 6, 2013, 6:26:45 AM3/6/13
to rocky, golan...@googlegroups.com
On 6 March 2013 07:26, rocky <rocky.b...@gmail.com> wrote:
> As my first go project, I'm just porting some code I've written in other
> languages which arranges an array nicely formatted into columns. See
> https://code.google.com/p/go-columnize/
>
> It works as is, but instead of passing list of strings, I'd like to pass a
> slice of anything and have the routine call fmt.Sprint to turn that into a
> list of strings. How do I do this?
>
> As a very naive first attempt, I tried:
>
> import "fmt"
> func print_array(a[] interface{}) {
> for i:=0 ; i<len(a); i++ {
> fmt.Println(a[i])
> }
> }
>
> c := [] int{1,2,3}
> print_array(c)
>
> But this gives a type mismatch.

Because a []int can't be assigned to a []interface{}.

[Suppose it could. Consider:

var spoo []interface{} = []int{1,2,17}
spoo[1] = "BOOM"

Oops! we have assigned a string into an int slice. That
can't be good. Rather than imposing run-time checks
on every assignment into an element of a []interface{},
and other like situations, we simply forbid it from
happening with a static type check.

"They have different stoage representations" is true
but is more of a permitted consequence rather than
being the underlying reason.
]


>
> I then look at fmt.print.go and see what is used there is p.doPrintf which
> has signature:
>
> func (p *pp) doPrintf(format string, a []interface{})
>
> I was hoping to use the existing structure there, but things like newPrinter
> and pp are private.
>
> Suggestions? Thanks.

Copy the []int into a new []interface{}.

Chris

--
Chris "DELIBERATELY not mentioning reflection" Dollin

Rocky Bernstein

unread,
Mar 6, 2013, 6:46:27 AM3/6/13
to chris dollin, golan...@googlegroups.com


This is very detailed on the why there is a type mismatch but a bit lacking in detail on the how to get this done.

The work of conversion should be done in the called side, in columnize, rather than on the caller side. It is not clear from the above which side this copy occurs, but I have a feeling it's on the wrong side.

I was originally going to phrase this as two separate questions. First, how to do this, and second, asking why something like this -- taking an array of anything and turning it into an array of string representation -- not already available. Possibly it would building off of fmt which seems to do this. Or perhaps if there is something already there, then perhaps fmt would use that as well.

If reflection was not mentioned because of the feeling that is overused or evil, then all the more reason to have things like this encapsulated. 

Dan Kortschak

unread,
Mar 6, 2013, 6:52:03 AM3/6/13
to Rocky Bernstein, chris dollin, golan...@googlegroups.com
Have you looked at tabwriter? http://golang.org/pkg/text/tabwriter It's in the standard library and it does pretty much what you are planning to implement, so it will either save you time, or give you an idea of how to approach what you want to do.

Nate Finch

unread,
Mar 6, 2013, 7:45:24 AM3/6/13
to golan...@googlegroups.com, Rocky Bernstein, chris dollin
The answer is, you can't really do it without a ton of reflection. That's what the fmt package does, and why it can print an array of anything. 

Probably the easiest way to do it is to just require the caller to convert their slice into a slice of interfaces and then you can just iterate over them and fmt.Sprint(s) them, joining them with tabs, and then write them out to a text/tabwriter.

Go is not very good at writing utility methods that can work on "anything". It generally requires a lot of annoying reflection. My response would be... do you *really* need a method that can work on anything? You'd probably be better off just taking a slice of string and let the callers convert their things to strings directly.  It's all going to be text eventually, so that's more direct anyway.

Rocky Bernstein

unread,
Mar 6, 2013, 9:31:37 AM3/6/13
to Dan Kortschak, chris dollin, golan...@googlegroups.com
On Wed, Mar 6, 2013 at 6:52 AM, Dan Kortschak <dan.ko...@adelaide.edu.au> wrote:
Have you looked at tabwriter? http://golang.org/pkg/text/tabwriter

Thanks! I have now looked at tabwriter. There may be ideas I can use from that. I'm not a fan of long parameter lists for passing options though as in the example: w.Init(os.Stdout, 0, 8, 0, '\t', 0)

It's in the standard library and it does pretty much what you are planning to implement,

Um, have you looked at columnize?  https://code.google.com/p/go-columnize/

I think you find on deeper inspection that they aren't really the same thing and this has already is implemented.

Rocky Bernstein

unread,
Mar 6, 2013, 9:53:01 AM3/6/13
to Nate Finch, golan...@googlegroups.com, chris dollin
On Wed, Mar 6, 2013 at 7:45 AM, Nate Finch <nate....@gmail.com> wrote:
The answer is, you can't really do it without a ton of reflection.

Ok. So get me started in the right first step. Print and friends accept  "a ... interface{}" which apparently then gets turned into a "a [] interface{}" in calls to p.doPrint() 

I was naively hoping to change "a... interface{}" into "a[] interface{}" but that's a type mismatch for all of the excellent reasons described before. 

So does that suggest that I have to start with a single "interface{}" and then do something to see that this is a list (or just assume so) and then covert that into something that is "[] interface{}" Then work like doPrint does? 
 
That's what the fmt package does, and why it can print an array of anything. 

And that's why I was hoping it or something like it would encapsulate a little more of that magic. fmt already does encapsulate a great bit of of the magic already. Something along the lines of just making doPrint public would go a ways towards this goal. 
 

Probably the easiest way to do it is to just require the caller to convert their slice into a slice of interfaces and then you can just iterate over them and fmt.Sprint(s) them, joining them with tabs, and then write them out to a text/tabwriter.

Go is not very good at writing utility methods that can work on "anything". It generally requires a lot of annoying reflection. My response would be... do you *really* need a method that can work on anything?

Let me answer your question with another question. Does Println (Sprint, ...)  *really* need to work on anything? My answer is absolutely technically no, but practically yes. And the same is true here. 

This is one relatively simple and short function. I don't mind jumping through some hoops to keep it simple on the user side. If it is simple to *use* -- like the print functions -- people are more likely to use it when they can. 
 
You'd probably be better off just taking a slice of string and let the callers convert their things to strings directly.  It's all going to be text eventually, so that's more direct anyway.

I'm not understanding this logic. If I have something that will accept anything it will accept a slice of string. People are always free then to "be direct" and do this first and I can probably expose that specific case as a direct call without the reflection.

Nate Finch

unread,
Mar 6, 2013, 10:54:40 AM3/6/13
to golan...@googlegroups.com, Nate Finch, chris dollin
On Wednesday, March 6, 2013 9:53:01 AM UTC-5, rocky wrote:
This is one relatively simple and short function. I don't mind jumping through some hoops to keep it simple on the user side. If it is simple to *use* -- like the print functions -- people are more likely to use it when they can.

Well, the implementation won't be short and simple, because of the need for reflection. The idea is simple, but this isn't python, and so working with a type that can be a slice of anything is somewhat tricky.
 
You'd probably be better off just taking a slice of string and let the callers convert their things to strings directly.  It's all going to be text eventually, so that's more direct anyway.

I'm not understanding this logic. If I have something that will accept anything it will accept a slice of string. People are always free then to "be direct" and do this first and I can probably expose that specific case as a direct call without the reflection.

The crux of the matter is that your function is actually doing two things:

  1. convert a slice of something into a slice of strings
  2. columnize those strings
Why do you want your function to do #1? Wouldn't that be better to be done by the caller, so that they have control of how their data is stringified?  It's trivial for the caller to write a small utility function to loop over whatever slice they have and convert the values to strings, and then give you those strings to columnize.  But if you do it yourself, you have to assume a specific format is ok, and then you get the problem of fmt.Print printing slices, where the format of the output is only ok if you don't really care about the format, and it seems like people calling columnize specifically do care how the output is formatted.

If you make the function take []string then you can simply loop over them and do your columnize magic to those strings. That's the code that is unique to columnize.

a := int[]{ 1, 2, 3, 4, 5 }
...
s := make([]string, 0, len(a))
for _, v := range a {
    s = append(s, strconv.Itoa(v))
}
output := Columnize(s)

func Columnize(s []string) string {
      // your columnize magic is easy, since you already have the strings formatted and divided up exactly how the caller wants, and you don't have to guess how the caller wants his slice of Foo formatted
}

Carlos Castillo

unread,
Mar 6, 2013, 1:34:51 PM3/6/13
to golan...@googlegroups.com
http://play.golang.org/p/bxdcIj6ueH


On Tuesday, March 5, 2013 11:26:46 PM UTC-8, rocky wrote:
As my first go project, I'm just porting some code I've written in other languages which arranges an array nicely formatted into columns. See https://code.google.com/p/go-columnize/

It works as is, but instead of passing list of strings, I'd like to pass a slice of anything and have the routine call fmt.Sprint to turn that into a list of strings.  How do I do this?

As a very naive first attempt, I tried:

    import "fmt"
    func print_array(a[] interface{}) {
        for i:=0 ; i<len(a); i++ {
            fmt.Println(a[i])
       }
    }   

    c := [] int{1,2,3}
    print_array(c)

But this gives a type mismatch.

rocky

unread,
Mar 6, 2013, 1:37:22 PM3/6/13
to golan...@googlegroups.com
Thanks!

Dan Kortschak

unread,
Mar 6, 2013, 3:29:40 PM3/6/13
to Rocky Bernstein, golan...@googlegroups.com
On Wed, 2013-03-06 at 09:31 -0500, Rocky Bernstein wrote:

>
> Um, have you looked at columnize?
> https://code.google.com/p/go-columnize/
>
>
> I think you find on deeper inspection that they aren't really the same
> thing and this has already is implemented.

I looked briefly, obviously I missed the significant differences.
Apologies.


Reply all
Reply to author
Forward
0 new messages