This does not make sense to me. Surely myint "implements" comparable,
> 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
I see. I primarily use C/asm, so I am familiar with the issues you're
referring to.
I'm not sure I see how this is possible for two reasons:
> When you pass a value of type myInt to a function expecting a value of
> type Comparable, the compiler is inserting a conversion operation.
(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.
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
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-----
>
>
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
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.
> You need something like this:ah, that helps.
>
> func Convert(in []T) []interface{} {
> out := make([]interface{}, len(in))
> for i, v := range in {
> out[i] = v
> }
> return out
> }
But why is this impossible to do implicitly? I don't see
anything specifing a size/memory layout in the above code?
thanks for the replies. I get it now.
grtz,
--
Miek
> 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
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
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
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.
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?
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.
so if I have a []string I can pass it to f([]interface{}) but not to
g([]interface{})?
> 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
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
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.
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.
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.
> 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