Function accepting a slice of interface types.

15,087 views
Skip to first unread message

Samuel Payson

unread,
Nov 8, 2010, 10:03:59 AM11/8/10
to golang-nuts
Hello everyone,
I have just recently started coding in Go, and I'm having trouble
understanding why a piece of code wont work. I am trying to implement
a general version of merge-sort using interfaces, but the program
won't compile.

The following is a simpler program which produces the same compilation
errors.

/***********************************************/
package main

type myInt int

type comparable interface {
LessThan(c comparable) bool
}

func (this myInt) LessThan(other myInt) (r bool) {
return this < other
}

func test(c []comparable) {
if c[0].LessThan(c[1]) {
print("lessThan\n")
} else {
print("greaterThan\n")
}
}

func main() {
slc := []myInt{1, 2}
test(slc)
}
/***********************************************/

Compilation produces the following:
$ 6g test.go
test.go:23: cannot use slc (type []myInt) as type []comparable in
function argument


This does not make sense to me. Surely myint "implements" comparable,
so what is the compiler complaining about? Is the concept of a slice
of an interface type just invalid in Go? If so, how would someone go
about implementing this concept in a non-cumbersome manner?

Thanks sincerely for any help,
Samuel C. Payson

JONNALAGADDA Srinivas

unread,
Nov 8, 2010, 10:37:23 AM11/8/10
to golang-nuts
Please try the following. Specifically, myInt's LessThan had
a different signature. Also, the slice in main() was not of type
Comparable.

                                                            Greetings,
                                                                   JS
* * * *

package main

import "fmt"

type myInt int

type Comparable interface {
LessThan(c Comparable) bool
}

func (this myInt) LessThan(other Comparable) (r bool) {
return this < other.(myInt)
}

func test(c []Comparable) {
if c[0].LessThan(c[1]) {
fmt.Print("lessThan\n")
} else {
fmt.Print("greaterThan\n")
}
}

func main() {
slc := []Comparable{myInt(1), myInt(2)}
test(slc)
}

Daniel Smith

unread,
Nov 8, 2010, 11:12:49 AM11/8/10
to Samuel Payson, golang-nuts


On Mon, Nov 8, 2010 at 9:03 AM, Samuel Payson <scpa...@gmail.com> wrote:
This does not make sense to me. Surely myint "implements" comparable,

Yes. But []comparable is not an interface, and if it were []myInt does not implement it.

Unfortunately, the way you have it set up you'll have to wrap each element:

slc := []comparable{myInt(1), myInt(2)}

Look at the sort package to see a more efficient (if also more awkward) way of doing what you want to do.

Samuel Payson

unread,
Nov 8, 2010, 3:57:44 PM11/8/10
to golang-nuts
Okay, I understand my mistake above. Thanks very much for such helpful
answers!

This seems like an arbitrary limitation to me, I can pass myInt to a
function accepting Comparable, but I can't pass []myInt to a function
accepting []Comparable. If I have a single myInt, it is effectively a
Comparable, but if I have multiple of them, they suddenly are no
longer Comparables. Is there an implementation-related reason that
this cannot work? I don't see this as 'breaking' any of the ideals of
Go, and it seems that the degree to which this would simplify many
aspects of writing Go code, as well as improve readability, is
considerable.

I know that, for the most part, those developing Go are pretty
confident about the way they are doing things, and have thought it all
through much more than I have. I am not saying that this element of
the language is something that should necessarily change, I would just
like to understand why things are this way. I find that understanding
the rationale behind such conventions makes them easier to use
properly and effectively.

Sincerely,
Samuel C. Payson

On Nov 8, 11:12 am, Daniel Smith <dan...@lukenine45.net> wrote:

Ian Lance Taylor

unread,
Nov 8, 2010, 4:18:08 PM11/8/10
to Samuel Payson, golang-nuts
Samuel Payson <scpa...@gmail.com> writes:

> This seems like an arbitrary limitation to me, I can pass myInt to a
> function accepting Comparable, but I can't pass []myInt to a function
> accepting []Comparable. If I have a single myInt, it is effectively a
> Comparable, but if I have multiple of them, they suddenly are no
> longer Comparables. Is there an implementation-related reason that
> this cannot work? I don't see this as 'breaking' any of the ideals of
> Go, and it seems that the degree to which this would simplify many
> aspects of writing Go code, as well as improve readability, is
> considerable.

There is a reason; I'm not sure whether to classify it as
implementation-related or language-related. A value of type Comparable
stored in memory does not look like a value of type myInt. As it
happens, they are not even the same size. Therefore, a value of type
[]Comparable does not look like a value of type []myInt, and you can't
index into a value of one type as though it were a value of the other
type.

When you pass a value of type myInt to a function expecting a value of
type Comparable, the compiler is inserting a conversion operation. This
is one of the relatively few implicit type conversions implemented by
Go. There is no reasonable type conversion from []myInt to
[]Comparable, because any plausible conversion would mean that any
changes to the []Comparable slice would not be reflected in the []myInt
slice.

In short, this boils down to a basic fact in Go: Go gives you control
over memory layout, and different types have different memory layout.
This is not true in languages like Java or Python, in which
approximately all values look the same.

Ian

Samuel Payson

unread,
Nov 8, 2010, 6:03:14 PM11/8/10
to golang-nuts
I see. I primarily use C/asm, so I am familiar with the issues you're
referring to.

> When you pass a value of type myInt to a function expecting a value of
> type Comparable, the compiler is inserting a conversion operation.
I'm not sure I see how this is possible for two reasons:
(1) Comparable is an interface, it has no data. Converting to it is
impossible (or just useless depending on how you look at it)
(2) Later when 'test(..)' above calls 'c[0].LessThan(c[1])', it is
using the 'myInt.LessThan(..)' method. For this reason the data needs
to still be of type myInt, or the function call is meaningless.

This also implies that some sort of runtime type identification is
going on (or maybe something goofy like pushing function pointers to
the stack). Similar methods could be used to identify the size of the
slice-members for indexing operations.

However, I have never implemented a compiler before, so I'm not sure
about the performance implications of this sort of system.

On Nov 8, 4:18 pm, Ian Lance Taylor <i...@google.com> wrote:

Steven

unread,
Nov 8, 2010, 7:33:03 PM11/8/10
to Samuel Payson, golang-nuts
On Mon, Nov 8, 2010 at 6:03 PM, Samuel Payson <scpa...@gmail.com> wrote:
I see. I primarily use C/asm, so I am familiar with the issues you're
referring to.

> When you pass a value of type myInt to a function expecting a value of
> type Comparable, the compiler is inserting a conversion operation.
I'm not sure I see how this is possible for two reasons:
(1) Comparable is an interface, it has no data. Converting to it is
impossible (or just useless depending on how you look at it)
(2) Later when 'test(..)' above calls 'c[0].LessThan(c[1])', it is
using the 'myInt.LessThan(..)' method. For this reason the data needs
to still be of type myInt, or the function call is meaningless.

This also implies that some sort of runtime type identification is
going on (or maybe something goofy like pushing function pointers to
the stack). Similar methods could be used to identify the size of the
slice-members for indexing operations.

However, I have never implemented a compiler before, so I'm not sure
about the performance implications of this sort of system.

An interface value boxes the data and uses dynamic dispatch via an itable (kind of like a vtable) to call methods. Converting to an interface just means boxing the value in an interface.

I'd suggest reading the language spec if you haven't already:

Its fairly easy to read, and well worth it in terms of all of our time (including yours) ;) Also check out the other documentation on that site.

Here's a look at how interfaces are implemented:

Currently, Go is straightforward when composing types. A slice of interfaces is just a slice whose elements happen to be interfaces. Your suggestion would break this property.

The reflect package offers a way to generically manipulate slices:

It does come with a loss of efficiency.

You could just roll your own by having an interface:

type Indexable interface {
     At(int) interface{}
     Set(int, interface{})
}

Note, this is where a couple of things would come in handy that Go doesn't have:

1) Slice methods: You can't currently define methods on an unnamed slice type as you can on an unnamed pointer type. This means you have to create a named slice type and convert to it in order to use its methods, which isn't a big obstacle, its just a little redundant.

2) Function polymorphism: At(int) T cannot be used as an At(int) interface{}. For functions, this can be solved with closures. However, for methods, there is no way to get around this for the purposes of satisfying an interface: the signature has to match. This means that you need to choose an interface return type to use, which is then incompatible with any other interface return type. You can use type assertions to get around this, and just use the most general return type, but its less elegant.

Samuel Payson

unread,
Nov 8, 2010, 9:08:30 PM11/8/10
to golang-nuts
Perfect, thanks very much, this is just the sort of information I was
looking for.

On Nov 8, 7:33 pm, Steven <steven...@gmail.com> wrote:

Miek Gieben

unread,
Nov 9, 2010, 5:28:02 PM11/9/10
to Ian Lance Taylor, Samuel Payson, golang-nuts
[ Quoting Ian Lance Taylor in "Re: [go-nuts] Re: Function acceptin"... ]

> There is a reason; I'm not sure whether to classify it as
> implementation-related or language-related. A value of type Comparable
> stored in memory does not look like a value of type myInt. As it
> happens, they are not even the same size. Therefore, a value of type
> []Comparable does not look like a value of type []myInt, and you can't
> index into a value of one type as though it were a value of the other
> type.

I'm trying to understand what you say here, but I fail...

func genarr(i []interface{}) {
for _, j := range i {
switch j.(type) {
case int:
fmt.Printf("Its a int\n")
case string:
fmt.Printf("Its a string\n")
}
}
}

func gen(i interface{}) {
switch i.(type) {
case int:
fmt.Printf("Its a int\n")
case string:
fmt.Printf("Its a string\n")
}
}

func main() {
gen(6)
gen("6")
// genarr([]int{6, 7, 5})
}

So for the gen() function it works, but not for genarr()?

Kind regards,
Miek

signature.asc

jimmy frasche

unread,
Nov 9, 2010, 5:35:16 PM11/9/10
to Ian Lance Taylor, Samuel Payson, golang-nuts
You can put a []T in an interface{} value but you cannot implictly
convert a []T to a []interface{} because they are different types with
different sizes and memory layouts

You need something like this:

func Convert(in []T) []interface{} {
out := make([]interface{}, len(in))
for i, v := range in {
out[i] = v
}
return out
}

> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
>
> iEYEARECAAYFAkzZyvIACgkQJYuFzziA0PYAXwCgkFOuyVMYGBrnw2LBOluXszWN
> MlkAmgPhsVAVcJgxDeB7xDwKwsN+aw0n
> =iKAo
> -----END PGP SIGNATURE-----
>
>

Miek Gieben

unread,
Nov 9, 2010, 5:48:32 PM11/9/10
to golang-nuts
[ Quoting jimmy frasche in "Re: [go-nuts] Re: Function acceptin"... ]

> You can put a []T in an interface{} value but you cannot implictly
> convert a []T to a []interface{} because they are different types with
> different sizes and memory layouts
>
> You need something like this:
>
> func Convert(in []T) []interface{} {
> out := make([]interface{}, len(in))
> for i, v := range in {
> out[i] = v
> }
> return out
> }

ah, that helps.

But why is this impossible to do implicitly? I don't see
anything specifing a size/memory layout in the above code?

regards,
Miek

signature.asc

jimmy frasche

unread,
Nov 9, 2010, 6:02:03 PM11/9/10
to golang-nuts
On Tue, Nov 9, 2010 at 2:48 PM, Miek Gieben <mi...@miek.nl> wrote:
> But why is this impossible to do implicitly? I don't see
> anything specifing a size/memory layout in the above code?

The type of the slice elements determines the size and layout of the
slice. A slice is basically a C array with extra information the
length and capacity where the size of the cells are determined by the
size of the element type.

It wouldn't be imposible to do implicitly but Go tends to do what you
say, no more and no less, and that's why I like it.

SnakE

unread,
Nov 9, 2010, 6:13:20 PM11/9/10
to golang-nuts
2010/11/10 Miek Gieben <mi...@miek.nl>

> You need something like this:
>
> func Convert(in []T) []interface{} {
>     out := make([]interface{}, len(in))
>     for i, v := range in {
>          out[i] = v
>     }
>     return out
> }

ah, that helps.

But why is this impossible to do implicitly? I don't see
anything specifing a size/memory layout in the above code?

Everything is possible, but the cost is significantly different.

An interface value is a two-field struct.  Ever.  First is a pointer to a type info with a function table.  Second is a pointer to the concrete value.  Or vice versa, the order is not important.

A string value is a single pointer.

When you pass a string to a function expecting `interface{}` the runtime constructs that two-field structure and passes it instead.  Asymptotic cost of this operation is amortized O(1).

An array of strings is internally a C array of pointers.  An array of `interface{}` internally is a C array of structs of two pointers each.  To convert `[]string` into `[]interface{}` the compiler would have to allocate another array, then convert each string individually.  The cost is suddenly O(n) + cost of an allocation which can be major in many different ways.

Steven

unread,
Nov 9, 2010, 6:16:52 PM11/9/10
to SnakE, golang-nuts
A string also has length...

All this, plus the fact that making a copy means the slice no longer has reference semantics.

Miek Gieben

unread,
Nov 9, 2010, 7:02:03 PM11/9/10
to golang-nuts
[ Quoting jimmy frasche in "Re: [go-nuts] Re: Function acceptin"... ]
> It wouldn't be imposible to do implicitly but Go tends to do what you
> say, no more and no less, and that's why I like it.

thanks for the replies. I get it now.

grtz,

--
Miek

signature.asc

Scott Pakin

unread,
Nov 9, 2010, 8:33:12 PM11/9/10
to golang-nuts
On Nov 9, 4:13 pm, SnakE <snake.sc...@gmail.com> wrote:
> When you pass a string to a function expecting `interface{}` the runtime
> constructs that two-field structure and passes it instead.  Asymptotic cost
> of this operation is amortized O(1).
>
> An array of strings is internally a C array of pointers.  An array of
> `interface{}` internally is a C array of structs of two pointers each.  To
> convert `[]string` into `[]interface{}` the compiler would have to allocate
> another array, then convert each string individually.  The cost is suddenly
> O(n) + cost of an allocation which can be major in many different ways.

But why can't Go simply box the entire array as a whole (at a cost of
O(1))? Per-access costs would be comparable to those incurred by
accessing a string passed to a function expecting an interface{}.
right?

I like the idea of being able to pass a []string (or whatever) to an
[]interface{} and don't see why this feature is inherently costly.

Ian Lance Taylor

unread,
Nov 9, 2010, 9:00:37 PM11/9/10
to Scott Pakin, golang-nuts
Scott Pakin <scot...@pakin.org> writes:

> On Nov 9, 4:13 pm, SnakE <snake.sc...@gmail.com> wrote:
>> When you pass a string to a function expecting `interface{}` the runtime
>> constructs that two-field structure and passes it instead.  Asymptotic cost
>> of this operation is amortized O(1).
>>
>> An array of strings is internally a C array of pointers.  An array of
>> `interface{}` internally is a C array of structs of two pointers each.  To
>> convert `[]string` into `[]interface{}` the compiler would have to allocate
>> another array, then convert each string individually.  The cost is suddenly
>> O(n) + cost of an allocation which can be major in many different ways.
>
> But why can't Go simply box the entire array as a whole (at a cost of
> O(1))? Per-access costs would be comparable to those incurred by
> accessing a string passed to a function expecting an interface{}.
> right?

Sure, you can box the entire array, but then you have an interface{}.
You don't have a []interface{}.

Ian

Scott Pakin

unread,
Nov 9, 2010, 9:22:30 PM11/9/10
to golang-nuts
On Nov 9, 7:00 pm, Ian Lance Taylor <i...@google.com> wrote:
> Sure, you can box the entire array, but then you have an interface{}.
> You don't have a []interface{}.

I meant internally to the Go implementation, not as something the user
would code up. That is, like the OP, I'd like to write "func test(c
[]comparable) {...}" and call it with "test([]myInt{1, 2})". I was
wondering why the Go implementation can't pass test() an (invisible,
as usual) interface value whose data field points to {1,2} and whose
tab field points to the comparable itable, just as in the scalar
case. The compiler knows it's being given an array, so it can
implement all array accesses like any other array, and with the help
of the itable, it can pass array elements to the appropriate myInt
methods.

Seems simple to me -- what am I missing?

Steven

unread,
Nov 9, 2010, 10:51:39 PM11/9/10
to Scott Pakin, golang-nuts
A slice of interfaces is a composite of an abstraction, not an abstraction of a composite. Changing this fundamentally alters the type system and the meaning of a slice. When you compose a type now, you can read it and see what it is. If you start adding all kinds of special behaviours that change the meaning of specific composed types, you end up with hard to analyse behaviour. []interface{} just means []interface{}, not something else. Putting an interface in a composed type should not make its behaviour fundamentally different than when that composed type contains any other type.

If you want abstractions of composite (and basic) types, look in the reflect package:

I think there does need to be a better way to handle abstracting over the built-in types. There is an oft promised (though with no time scale) rewrite to the reflect package to make it more, well, gooder, in many as-of-yet undefined ways. Here's to the day :)

Ian Lance Taylor

unread,
Nov 9, 2010, 11:07:34 PM11/9/10
to Scott Pakin, golang-nuts
Scott Pakin <scot...@pakin.org> writes:

It can't implement the array accesses like any other array, because the
size of the elements would vary depending on the type of the argument.
It could implement them differently, with multiplications, the way
reflect.ArrayOrSliceValue.Elem works, but I think it would be somewhat
surprising if it were to do that automatically.

Ian

Andreas Rossberg

unread,
Nov 10, 2010, 7:36:59 AM11/10/10
to golang-nuts
There are at least three problems with your code. First, this:

>     slc := []myInt{1, 2}

as already pointed out, does not work. You need to say:

>     slc := []comparable{myInt{1}, myInt{2}}

What you get is an array of comparables. Which leads to the second
problem: there is no guarantee that a []comparable contains only
myInts -- even if some of its slots are myInt. There is nothing from
preventing you (or somebody else) to do

type myBool bool
func (this myBool) lessThan(that comparable) { return false; }

heterogeneous := []comparable{myInt{1}, myBool{true}}
test(heterogeneous)

Which leads to the third problem: your lessThan method for myInt does
not actually implement the comparable interface. The comparable
interface does not put any constraints on what the type of lessThan's
argument is, so if c is a comparable, I should be able to call

c.lessThan(myBool{true})

Since your method does not allow that, it's type is incompatible with
the comparable interface. It would need to accept an arbitrary
comparable as argument (like my lessThan for myBool above). Of course,
this makes it much less useful.

This is the good old binary method problem. It cannot really be solved
in Go unless

1. you are willing to give up static type safety and use a type switch
inside your method,
2. or Go is extended with parametric polymorphism (aka generics).

/Andreas

Scott Pakin

unread,
Nov 10, 2010, 12:45:03 PM11/10/10
to golang-nuts
On Nov 9, 9:07 pm, Ian Lance Taylor <i...@google.com> wrote:
> It can't implement the array accesses like any other array, because the
> size of the elements would vary depending on the type of the argument.
> It could implement them differently, with multiplications, the way
> reflect.ArrayOrSliceValue.Elem works, but I think it would be somewhat
> surprising if it were to do that automatically.

How are arrays of known datatypes accessed if not with
multiplications? If a function accepts "myArray []int32", you're
saying that myArray[i] isn't normally accessed using something like
myArray+i*4?

chris dollin

unread,
Nov 10, 2010, 12:48:44 PM11/10/10
to Scott Pakin, golang-nuts

The multipliers are normally constants. Also, the size of the
objects is known in advance, so loading/storing them is
easy peasy; if you don't know the type till runtime, you don't
know the multiplier AND you don't know how to load & store
elements. So you have to use code that can cope with any-size
objects.

Chris

--
Chris "allusive" Dollin

Nigel Tao

unread,
Nov 10, 2010, 6:19:37 PM11/10/10
to Scott Pakin, golang-nuts
On 11 November 2010 04:45, Scott Pakin <scot...@pakin.org> wrote:
> How are arrays of known datatypes accessed if not with
> multiplications?  If a function accepts "myArray []int32", you're
> saying that myArray[i] isn't normally accessed using something like
> myArray+i*4?

I think it's not just the memory location, it's also how to interpret
the bits at that memory location.

Suppose I have

type I interface{}
func f(z []I) { /*do something with z*/ }
x := make([]int, 3)
f(x) // fails

Trying to use x as an []I fails even though int satisfies I. Even if
you know that elements of an []int are 4 bytes apart and elements of
[]I are 8 bytes apart, even if you can shim it so the z that f sees
intercepts z[2] to look at the memory location ptr+2*4 instead of
ptr+2*8, the 4 bytes you see there aren't an interface value: there is
no itable.

You could tweak your shim so that it autoboxed on reads, so that while
x[2] takes up 4 bytes, the statement
_ = z[2]
created a temporary interface value that was 8 bytes and had an
itable. But what if f writes to z as well as reads from z? If f was:
z[2] = "notAnInt"; _ = z[2].(string)
then your shim needs to be able to both autobox the underlying int and
be able to replace individual elements by things that aren't ints. But
it can't write to the underlying slice because you can't assign a
string to an int. And then you're in a weird world where f "always"
modifies the contents of the slice it is passed, except x isn't
modified because it's been magically replaced by a clumsy,
inefficient, non-trivial, semantics-changing shim.

Instead, if you need to convert a []int to a []interface{}, just be
explicit and do the obvious loop. Or use reflection with the
understanding that it won't be as efficient.

Scott Pakin

unread,
Nov 10, 2010, 7:52:21 PM11/10/10
to golang-nuts
On Nov 10, 10:48 am, chris dollin <ehog.he...@googlemail.com> wrote:
> The multipliers are normally constants. Also, the size of the
> objects is known in advance, so loading/storing them is
> easy peasy; if you don't know the type till runtime, you don't
> know the multiplier AND you don't know how to load & store
> elements. So you have to use code that can cope with any-size
> objects.

Yes, that makes sense. However, the inability to use a constant
multiplier and the extra type check needed to store an element
represents a fixed per-element access cost, which I'd think would be
roughly equivalent to the cost incurred when passing a scalar to a
function that accepts an interface. That is, it doesn't seem like
there'd be a huge performance difference between defining "func
test(a,b,c,d,e comparable)" (cf. the original post in this thread) and
accessing the five arguments versus defining "func test(many
[]comparable{})" and accessing the first five elements of the many
argument.

Scott Pakin

unread,
Nov 10, 2010, 8:15:34 PM11/10/10
to golang-nuts
On Nov 10, 4:19 pm, Nigel Tao <nigel.tao.gn...@gmail.com> wrote:
> Suppose I have
>
> type I interface{}
> func f(z []I) { /*do something with z*/ }
> x := make([]int, 3)
> f(x) // fails
>
> Trying to use x as an []I fails even though int satisfies I. Even if
> you know that elements of an []int are 4 bytes apart and elements of
> []I are 8 bytes apart, even if you can shim it so the z that f sees
> intercepts z[2] to look at the memory location ptr+2*4 instead of
> ptr+2*8, the 4 bytes you see there aren't an interface value: there is
> no itable.

Good point, but what if the Go implementation could pass a "virtual"
interface structure alongside an []interface{} argument? This could
contain the same itable that you'd get by passing a scalar to an
interrface{} argument so the target function could call the correct
methods on an array element (and know the proper size to use in the
multiplication to find the array element).

I'm still trying to get my head around whether the opposition to
allowing functions to accept arrays of interface types is (a) a
performance issue, (b) a complexity of implementation issue, or (c)
something that violates the Go philosophy of avoiding costs that a
programmer inherently can't reason about.

jimmy frasche

unread,
Nov 10, 2010, 8:33:33 PM11/10/10
to Scott Pakin, golang-nuts

Seems like a little of all three but the last is the strongest
argument against me.

Also would this /just/ be for []interface{}, introducing a special
case that's sometimes useful for some people, or for any concrete T
being passed as an interface I? What about from []I to []T? What about
between two interface types? Two concrete types? What happens when I
add an element of type S to one of these proxies of a []T?

When would it be okay? What happens if a function of []interface{} is
passed a []T and then calls another function?

How often does this really come up in actual code? Is it really worth
the complications to the spec and implementation? Is it general
enough? Could this be solved another way, like allowing
[]interface{}(slice_of_something_else) to generate the code I gave
before instead of creating an invisible proxy object to satisfy
assignment compatiability?

Nigel Tao

unread,
Nov 10, 2010, 10:14:31 PM11/10/10
to Scott Pakin, golang-nuts
On 11 November 2010 12:15, Scott Pakin <scot...@pakin.org> wrote:
> Good point, but what if the Go implementation could pass a "virtual"
> interface structure alongside an []interface{} argument?  This could
> contain the same itable that you'd get by passing a scalar to an
> interrface{} argument so the target function could call the correct
> methods on an array element (and know the proper size to use in the
> multiplication to find the array element).

That might work for reads, but I don't think it works for writes. To
repeat my earlier example, what should this program do:

-------------
f := func(z []interface{}) {
z[2] = "notAnInt"
println(z[2])


}
x := make([]int, 3)
f(x)

println(x[2])
-------------

f looks like a perfectly reasonable function, since string satisfies
interface{}, but you can't store a string in x.


> I'm still trying to get my head around whether the opposition to
> allowing functions to accept arrays of interface types is (a) a
> performance issue, (b) a complexity of implementation issue, or (c)
> something that violates the Go philosophy of avoiding costs that a
> programmer inherently can't reason about.

I think it's the sum of all three, plus the question I repeated above.

Scott Pakin

unread,
Nov 11, 2010, 4:51:27 PM11/11/10
to golang-nuts
On Nov 10, 6:33 pm, jimmy frasche <soapboxcic...@gmail.com> wrote:
> Also would this /just/ be for []interface{}, introducing a special
> case that's sometimes useful for some people, or for any concrete T
> being passed as an interface I? What about from []I to []T? What about
> between two interface types? Two concrete types? What happens when I
> add an element of type S to one of these proxies of a []T?

I'd think the same rules that apply to scalars should apply in all of
these cases.

> When would it be okay? What happens if a function of []interface{} is
> passed a []T and then calls another function?

What happens if a function of interface{} is passed a T and then calls
another function? That works, right? So it should work similarly in
the case of an []interface{} being passed a []T and calling another
function.

> How often does this really come up in actual code? Is it really worth
> the complications to the spec and implementation? Is it general
> enough? Could this be solved another way, like allowing
> []interface{}(slice_of_something_else) to generate the code I gave
> before instead of creating an invisible proxy object to satisfy
> assignment compatiability?

I don't know. Clearly, it's come up at least once, as evidenced by
the post that started this thread.

While the implementation may become more complicated, I think the spec
would become less complicated. Currently, Go is making a special case
for scalars that it doesn't make for any other type. The Go language
would be *more* self-consistent, not less, if the same semantics that
apply to scalars applied to arrays.

Scott Pakin

unread,
Nov 11, 2010, 4:58:51 PM11/11/10
to golang-nuts
On Nov 10, 8:14 pm, Nigel Tao <nigel.tao.gn...@gmail.com> wrote:
> That might work for reads, but I don't think it works for writes. To
> repeat my earlier example, what should this program do:
>
> -------------
> f := func(z []interface{}) {
>   z[2] = "notAnInt"
>   println(z[2])}
>
> x := make([]int, 3)
> f(x)
> println(x[2])
> -------------
>
> f looks like a perfectly reasonable function, since string satisfies
> interface{}, but you can't store a string in x.

I'd think it should fail. x has a concrete type with a known layout
in memory. Passing x to f() and binding it to a z of type
[]interface{} shouldn't affect either the type or layout of x. f() is
just saying it doesn't know at compile time what concrete type it'll
be given. At run time, z has a concrete type: an array of ints, each
of which properly implements the empty interface. Assigning a string
to z[2] should therefore be no different from assigning a string to
x[2].

jimmy frasche

unread,
Nov 11, 2010, 5:20:55 PM11/11/10
to Scott Pakin, golang-nuts

so if I have a []string I can pass it to f([]interface{}) but not to
g([]interface{})?

Ian Lance Taylor

unread,
Nov 11, 2010, 5:36:59 PM11/11/10
to Scott Pakin, golang-nuts
Scott Pakin <scot...@pakin.org> writes:

> While the implementation may become more complicated, I think the spec
> would become less complicated. Currently, Go is making a special case
> for scalars that it doesn't make for any other type. The Go language
> would be *more* self-consistent, not less, if the same semantics that
> apply to scalars applied to arrays.

I don't agree. The current rule is simple: you can assign a value of
type T to a variable of type I if the type T satisfies the interface I.
Calling a function applies the assignment rule from the function call
arguments to the funtion parameters.

You appear to be saying that you can also assign a value of type []T to
a variable of type []I if the type T satisfies the interface I. Only I
don't think that is actually what you are suggesting, because that
definitely won't work. I think what you are suggesting is that if a
function has a parameter of type []I, then you can invoke it with an
argument of type []T if the type T satisfies the interface I. That
introduces a new rule which is specific to function calls. I don't
think that is more self-consistent.

And your proposal, at least so far, is specific to slice types. Why
should it be specific to slice types, and not to array, map, and channel
types? In particular, if a function has an argument of chan I, can I
call it with an argument of chan T, if T satisfies I?

Ian

Ian Lance Taylor

unread,
Nov 11, 2010, 5:39:32 PM11/11/10
to Scott Pakin, golang-nuts
Scott Pakin <scot...@pakin.org> writes:

But there is a real difference. Assigning a string to x[2] fails at
compile time. Assigning a string to z[2] only fails at run time. One
aspect of Go is that it carefully indicates which operations can fail at
run time. E.g., a simple assignment can never fail at runtime. A type
assertion can.

Failing to maintain that clear distinction between compile time failures
and runtime failures is one of the key difficulties of Java's generics
implementation.

Ian

Nigel Tao

unread,
Nov 11, 2010, 5:42:40 PM11/11/10
to Scott Pakin, golang-nuts
On 12 November 2010 08:58, Scott Pakin <scot...@pakin.org> wrote:
> On Nov 10, 8:14 pm, Nigel Tao <nigel.tao.gn...@gmail.com> wrote:
>> That might work for reads, but I don't think it works for writes. To
>> repeat my earlier example, what should this program do:
>>
>> -------------
>> f := func(z []interface{}) {
>>   z[2] = "notAnInt"
>>   println(z[2])}
>>
>> x := make([]int, 3)
>> f(x)
>> println(x[2])
>> -------------
>>
>> f looks like a perfectly reasonable function, since string satisfies
>> interface{}, but you can't store a string in x.
>
> I'd think it should fail.

You see, I would have thought that it should succeed: z[2] has type
interface{}, so why can't I assign a string to it?

The fact that reasonable people can argue about how the corner cases
work suggests to me that the idea isn't worth it unless the benefits
are significant, and I don't think the benefits are significant since
doing the explicit conversion is trivial.


> x has a concrete type with a known layout
> in memory.  Passing x to f() and binding it to a z of type
> []interface{} shouldn't affect either the type or layout of x.  f() is
> just saying it doesn't know at compile time what concrete type it'll
> be given.  At run time, z has a concrete type: an array of ints, each
> of which properly implements the empty interface.  Assigning a string
> to z[2] should therefore be no different from assigning a string to
> x[2].

Here's another example. What should it print?

-------------
f := func(z []interface{}) *interface{} {
return &z[2]


}
x := make([]int, 3)

println(f(x))
-------------

Currently, slices and interfaces work orthogonally: slices are 3 words
(a base pointer, length and cap) and the stride of the underlying
array is known at compile time. Interfaces are 2 words (an itable and
value). The implementation of
value := slice[i]
is the same regardless of whether it's a slice of interfaces or a
slice of something else: (perform a bounds check and) copy the bits
from location basePtr + i*stride into value. An assignment like
"slice[i] = value" is similarly cheap and straightforward.

If you allow a []int to masquerade as an []interface{} then it gets
more complicated. Slices have to have extra storage to say whether
they're a real slice or a fake slice, and if they're faking, the
itable of what they're posing as. Reading slice[i] needs to check the
fake bit and synthesize an interface value from two separate memory
locations. Writing slice[i] also has to check compatibility and then
maybe do a partial copy. It just makes things more complicated.

Sure, I can see why you'd want to automatically promote []T to []I,
but I think the benefit is marginal since the explicit conversion is
trivial, and ultimately the benefits don't outweigh the costs.

Scott Pakin

unread,
Nov 11, 2010, 8:28:51 PM11/11/10
to golang-nuts
On Nov 11, 3:20 pm, jimmy frasche <soapboxcic...@gmail.com> wrote:
> so if I have a []string I can pass it to f([]interface{}) but not to
> g([]interface{})?

What's g here? A function that f invokes? If so, then I'd say it
should be okay. The *value* being passed has a type ([]string), even
the variable receiving it specifies only an interface to that value,

jimmy frasche

unread,
Nov 11, 2010, 8:33:33 PM11/11/10
to Scott Pakin, golang-nuts

My point was that it doesn't matter what they are because the func's
type signature is no longer enough to tell you what will happen when
you pass it a slice proxied into a []interface{}. f and g could both
modify the slice but maybe f just happens to append a string while g
just happens to append an int. And if f invokes g on some condition it
could be okay for f to pass the proxied slice to g 99 times and then
blow up on the 100th.

Scott Pakin

unread,
Nov 11, 2010, 8:37:00 PM11/11/10
to golang-nuts
On Nov 11, 3:36 pm, Ian Lance Taylor <i...@google.com> wrote:
> You appear to be saying that you can also assign a value of type []T to
> a variable of type []I if the type T satisfies the interface I.  Only I
> don't think that is actually what you are suggesting, because that
> definitely won't work.  I think what you are suggesting is that if a
> function has a parameter of type []I, then you can invoke it with an
> argument of type []T if the type T satisfies the interface I.  That
> introduces a new rule which is specific to function calls.  I don't
> think that is more self-consistent.

I'll admit I had been thinking only about function calls, but you're
right that the same rules should apply to ordinary assignments.

> And your proposal, at least so far, is specific to slice types.  Why
> should it be specific to slice types, and not to array, map, and channel
> types?  In particular, if a function has an argument of chan I, can I
> call it with an argument of chan T, if T satisfies I?

It'd be great if it applied everywhere. Consider a recursive
definition: You can assign a value of type T to a variable of type I
if the type T satisfies the interface I. You can assign an aggregate
(slice, array, map, channel) of type T to an aggregate of type I if
the type T satisfies the interface I. And so forth for aggregates of
aggregates.

Scott Pakin

unread,
Nov 11, 2010, 8:39:43 PM11/11/10
to golang-nuts
On Nov 11, 3:39 pm, Ian Lance Taylor <i...@google.com> wrote:
> But there is a real difference.  Assigning a string to x[2] fails at
> compile time.  Assigning a string to z[2] only fails at run time.  One
> aspect of Go is that it carefully indicates which operations can fail at
> run time.  E.g., a simple assignment can never fail at runtime.  A type
> assertion can.

Yes, that's a real difference. However, like a type assertion, it's
an easy-to-state difference: assigning to a variable of interface type
can fail at runtime. Assigning to a concrete type can fail at compile
time.

jimmy frasche

unread,
Nov 11, 2010, 8:45:50 PM11/11/10
to Scott Pakin, golang-nuts
What you really seem to want is slices with a generic type. I don't
see any reason why this needs to be jammed in elsewhere as a confusing
half-solution.

Scott Pakin

unread,
Nov 11, 2010, 9:17:08 PM11/11/10
to golang-nuts
On Nov 11, 3:42 pm, Nigel Tao <nigel.tao.gn...@gmail.com> wrote:
> On 12 November 2010 08:58, Scott Pakin <scott+...@pakin.org> wrote:
> You see, I would have thought that it should succeed: z[2] has type
> interface{}, so why can't I assign a string to it?

z[2] refers to a value of a specific type that implements
interface{}. It doesn't refer to an empty bucket that can be filled
with anything.

It's a bit like a void * in C; it can point to a value of any type,
but that doesn't mean you can (safely) overwrite its target with a
value of arbitrary size.

> Here's another example. What should it print?
>
> -------------
> f := func(z []interface{}) *interface{} {
>   return &z[2]}
>
> x := make([]int, 3)
> println(f(x))
> -------------

Intuitively, I'd think it would return &x[2]. While all f() knows
about z is that it's an array of values that implement the empty
interface, those values do have a concrete type (int, in this case),
and that type doesn't change, even if f() doesn't know at compile time
what it is.

> If you allow a []int to masquerade as an []interface{} then it gets
> more complicated. Slices have to have extra storage to say whether
> they're a real slice or a fake slice, and if they're faking, the
> itable of what they're posing as. Reading slice[i] needs to check the
> fake bit and synthesize an interface value from two separate memory
> locations. Writing slice[i] also has to check compatibility and then
> maybe do a partial copy. It just makes things more complicated.

I'm not sure you'd need "real" and "fake" slices. Values have types,
and interfaces know what types they represent. Go would just have to
ensure that it hoists that information to an aggregate of interfaces.

> Sure, I can see why you'd want to automatically promote []T to []I,
> but I think the benefit is marginal since the explicit conversion is
> trivial, and ultimately the benefits don't outweigh the costs.

Perhaps, but that's what I'm trying to learn from this thread, if
passing an aggregate of some type to an aggregate of an interface is
not in Go because its semantics is inherently ill-defined or
contradicts other Go semantics; it incurs unintuitive performance
costs; or if it's just because the language designers hadn't thought
of it or because the changes needed for the compiler would overwhelm
the available manpower. Clearly, the addition of any language feature
requires careful thought, and I realize now that this feature is
trickier than I had anticipated. However, the underlying idea still
doesn't smack of being impossible or fundamentally anti-Go, at least
to me.

Scott Pakin

unread,
Nov 11, 2010, 9:28:01 PM11/11/10
to golang-nuts
That's good food for thought. My initial jump into (hijack of?) this
thread was because it seemed inconsistent that one can pass three type
Ts to a function that accepts three interface Is but one can't pass an
array of three Ts to a function that accepts an array of three Is. I
suppose with generics, the argument passing would work, but I see one
difference: Suppose interface I is not the empty interface but an
interface that implements particular methods, which the function then
calls. Do you think that could be made to work with generics? The
arguments aren't really "generic" at that point.

Scott Pakin

unread,
Nov 11, 2010, 9:33:17 PM11/11/10
to golang-nuts
On Nov 11, 6:33 pm, jimmy frasche <soapboxcic...@gmail.com> wrote:
> My point was that it doesn't matter what they are because the func's
> type signature is no longer enough to tell you what will happen when
> you pass it a slice proxied into a []interface{}. f and g could both
> modify the slice but maybe f just happens to append a string while g
> just happens to append an int. And if f invokes g on some condition it
> could be okay for f to pass the proxied slice to g 99 times and then
> blow up on the 100th.

Yes, and I believe this is a similar criticism to Ian's above: The
checking fundamentally has to be done at run time, so a function that
succeeds 99 times can blow up on the 100th iteration, just like a Go
type assertion. But if the rule were, "Assigning values to interface
values can fail at run time," programmers would not be taken by
surprise; they always know when they're using an interface and when
they're not.

Nigel Tao

unread,
Nov 11, 2010, 9:33:59 PM11/11/10
to Scott Pakin, golang-nuts
On 12 November 2010 13:17, Scott Pakin <scot...@pakin.org> wrote:
> On Nov 11, 3:42 pm, Nigel Tao <nigel.tao.gn...@gmail.com> wrote:
>> Here's another example. What should it print?
>>
>> -------------
>> f := func(z []interface{}) *interface{} {
>>   return &z[2]}
>> }
>> x := make([]int, 3)
>> println(f(x))
>> -------------
>
> Intuitively, I'd think it would return &x[2].

But &x[2] is the address of a 4-byte bucket (assuming 32-bit words),
and *interface{} expects an 8-byte bucket. What if you extend the
example to be:

-------------
f := func(z []interface{}) *interface{} {
return &z[2]
}
x := make([]int, 3)

y := f(x) // y has type *interface{}
*y = "notAnInt" // strings implement interface{}
-------------

If y is just the location of &x[2] then how does the last assignment
know, at runtime, that the bucket at that location is the wrong type?
Trying to write an 8-byte interface value would clobber the memory
past &x[2]. Does it matter if the final line was "*y = 7" instead?


> While all f() knows about z is that it's an array of values

Nit: there's a difference, in Go, between arrays and slices. [100]int
is an array, []int is a slice. The former takes up 400 bytes, the
latter takes up 12 bytes and 4 of those bytes points to somewhere
inside an array.


> Perhaps, but that's what I'm trying to learn from this thread, if
> passing an aggregate of some type to an aggregate of an interface is
> not in Go because its semantics is inherently ill-defined or
> contradicts other Go semantics; it incurs unintuitive performance
> costs; or if it's just because the language designers hadn't thought
> of it or because the changes needed for the compiler would overwhelm
> the available manpower.

They're all valid costs that add up. It's not like only one of those
things is the deal-breaker.

Ian Lance Taylor

unread,
Nov 12, 2010, 1:18:59 AM11/12/10
to Scott Pakin, golang-nuts
Scott Pakin <scot...@pakin.org> writes:

> I'll admit I had been thinking only about function calls, but you're
> right that the same rules should apply to ordinary assignments.

At that point I think you would have to say that a slice of an interface
type is fundamentally different from a slice of a value type.
Operations on such slices would behave somewhat differently than
operations on regular slices. It would be necessary to carefully go
through all the operations and figure out how they should work.


> It'd be great if it applied everywhere. Consider a recursive
> definition: You can assign a value of type T to a variable of type I
> if the type T satisfies the interface I. You can assign an aggregate
> (slice, array, map, channel) of type T to an aggregate of type I if
> the type T satisfies the interface I. And so forth for aggregates of
> aggregates.

Then you have to do the same sort of analysis for every aggregate type.
It's probably doable, but I don't think it can be pushed under the
table.


>On Nov 11, 3:39 pm, Ian Lance Taylor <i...@google.com> wrote:
>> But there is a real difference.  Assigning a string to x[2] fails at
>> compile time.  Assigning a string to z[2] only fails at run time.  One
>> aspect of Go is that it carefully indicates which operations can fail at
>> run time.  E.g., a simple assignment can never fail at runtime.  A type
>> assertion can.

>Yes, that's a real difference. However, like a type assertion, it's
>an easy-to-state difference: assigning to a variable of interface type
>can fail at runtime. Assigning to a concrete type can fail at compile
>time.

You need more than that. In Go, if the operation can fail at runtime,
there must be some sort of ,ok statement which can be used to test
whether the operation will fail.

Also, more stylistically, I don't think it fits well with the rest of
the language if a specific operation, like the assignment operation, can
sometimes succeed and sometimes fail. Note that there is no such
operation in the language at present. The operations which may fail at
runtime are clearly delineated from the operations which may only fail
at compile time.


>That's good food for thought. My initial jump into (hijack of?) this
>thread was because it seemed inconsistent that one can pass three type
>Ts to a function that accepts three interface Is but one can't pass an
>array of three Ts to a function that accepts an array of three Is. I
>suppose with generics, the argument passing would work, but I see one
>difference: Suppose interface I is not the empty interface but an
>interface that implements particular methods, which the function then
>calls. Do you think that could be made to work with generics? The
>arguments aren't really "generic" at that point.

There are languages which work that way. E.g., C# generics have a
similar feature: you can, indeed must, describe precisely which features
the dynamic type must support in order to be able to use it as the
generic type. During the C++0x process this idea was introduced under
the name of concepts, although it was dropped from the final draft.

Ian

Andreas Rossberg

unread,
Nov 12, 2010, 7:35:49 AM11/12/10
to golang-nuts
On Nov 12, 3:28 am, Scott Pakin <scott+...@pakin.org> wrote:
>
> That's good food for thought.  My initial jump into (hijack of?) this
> thread was because it seemed inconsistent that one can pass three type
> Ts to a function that accepts three interface Is but one can't pass an
> array of three Ts to a function that accepts an array of three Is.

The difference you observe there is not due to tuples vs arrays as
such, but due to immutable vs mutable types. In terms of type theory:
assume that type T is a subtype of U (Go doesn't really have
subtyping, but assignability is similar enough in this context). Then
the tuple type (T, T, T) would be a subtype of the tuple type (U, U,
U), provided tuples are immutable (think of argument lists as
immutable tuples). Likewise, an array type []T would be a subtype of
[]U -- if arrays were immutable. But it is a fundamental necessity for
a sound type system that a MUTABLE array type []T is NOT a subtype of
[]U.

FWIW, this principle was violated in Java with its covariant arrays,
which essentially break soundness, require runtime checks on array
assignment, and caused major pain when generics where added later.

Scott Pakin

unread,
Nov 12, 2010, 7:01:19 PM11/12/10
to golang-nuts
On Nov 12, 5:35 am, Andreas Rossberg <rossb...@google.com> wrote:
> The difference you observe there is not due to tuples vs arrays as
> such, but due to immutable vs mutable types. In terms of type theory:
> assume that type T is a subtype of U (Go doesn't really have
> subtyping, but assignability is similar enough in this context). Then
> the tuple type (T, T, T) would be a subtype of the tuple type (U, U,
> U), provided tuples are immutable (think of argument lists as
> immutable tuples). Likewise, an array type []T would be a subtype of
> []U -- if arrays were immutable. But it is a fundamental necessity for
> a sound type system that a MUTABLE array type []T is NOT a subtype of
> []U.

Ah, that's a distinction I hadn't thought of and a good summary of
what's (dis)allowed vis a vis passing concrete types to interfaces.
Alongside Nigel's observation that lots of state would have to be
carried along with every variable and Ian's point that even ordinary
assignment statements would have to support a run-time failure mode, I
think I'm ready to throw in the towel. It looks like interfacing
mutable types really would violate various aspects of Go's design
philosophy, and there doesn't seem to be any implementation technique
that can work around that.

Thanks to everyone for the enlightening discussion. I feel I learned
something here.
Reply all
Reply to author
Forward
0 new messages