> I think it's important to realize that there are different ways to
> implement generics. In order to be able to use generics to make the
> standard library much faster at runtime, you need to use an
> implementation along the lines of C++ templates, in which each function
> that uses a generic type is in effect rewritten to use that type and
> then recompiled. This approach means that compilation slows down.
I don't quite get this argument of the 'slow' compilation. When the
programmer wants the best performance, he or she needs to implement the
specialized functions as well, so in effect, the final amount of code
remains the same. no? Or would the rewriting of the generic code be such a
bottleneck for the compiler? I can hardly imagine that.
On Friday, April 27, 2012 2:31:21 AM UTC+8, notnot wrote:
>> I think it's important to realize that there are different ways to >> implement generics. In order to be able to use generics to make the >> standard library much faster at runtime, you need to use an >> implementation along the lines of C++ templates, in which each function >> that uses a generic type is in effect rewritten to use that type and >> then recompiled. This approach means that compilation slows down.
> I don't quite get this argument of the 'slow' compilation. When the > programmer wants the best performance, he or she needs to implement the > specialized functions as well, so in effect, the final amount of code > remains the same. no? Or would the rewriting of the generic code be such a > bottleneck for the compiler? I can hardly imagine that.
I was about to write exactly that.
The compiler wouldn't add/compile any more code than what the programmer will be forced to add manually anyway.
On Thu, Apr 26, 2012 at 11:31 AM, Erwin <snesse...@gmail.com> wrote:
>> I think it's important to realize that there are different ways to
>> implement generics. In order to be able to use generics to make the
>> standard library much faster at runtime, you need to use an
>> implementation along the lines of C++ templates, in which each function
>> that uses a generic type is in effect rewritten to use that type and
>> then recompiled. This approach means that compilation slows down.
> I don't quite get this argument of the 'slow' compilation. When thet
> programmer wants the best performance, he or she needs to implement the
> specialized functions as well, so in effect, the final amount of code
> remains the same. no? Or would the rewriting of the generic code be such a
> bottleneck for the compiler? I can hardly imagine that.
If we rewrite the sort package to use generics, then everybody using the sort
package gets the slower compilation, even if they don't care about performance.
In other words, adding generics to packages slows down compilation for
everybody,
even people who would prefer fast compilation to fast runtime performance.
Rewriting the generic code is not a bottleneck, but compiling the generic code
multiple times is. Also the question arises of when the code is
compiled; if done
at compile time, as most C++ compilers do, then if two different
packages want to
sort ints, the compiler has to compile the sort<int> code twice.
These issues may seem minor, but remember that fast compilation is a major
goal of the Go project, and that we want Go to be fast even when building large
projects. One of the reasons C++ does not have fast compilation
is that the standard library is heavily templatized and the classes are compiled
many times when building a large project.
> Rewriting the generic code is not a bottleneck, but compiling the generic code
> multiple times is. Also the question arises of when the code is
> compiled; if done
> at compile time, as most C++ compilers do, then if two different
> packages want to
> sort ints, the compiler has to compile the sort<int> code twice.
If there were a C++-style, compile-time generics proposal for Go,
which somehow managed to abide "compile only once per concrete type"
semantics, would that be a "winner" from your perspective?
> These issues may seem minor, but remember that fast compilation is a major
> goal of the Go project, and that we want Go to be fast even when building large
> projects. One of the reasons C++ does not have fast compilation
> is that the standard library is heavily templatized and the classes are compiled
> many times when building a large project.
"Rewriting the generic code is not a bottleneck, but compiling the generic code multiple times is. Also the question arises of when the code is compiled; if done at compile time, as most C++ compilers do, then if two different packages want to sort ints, the compiler has to compile the sort<int> code twice. "
Why should be sort<int> compiled more than once? When compiling the first time you cache it, the object is available somewhere (as a .a static library or the go compiler produced equivalent) to be linked by the other packages that need sort<int>.
In fact it should be like a package appendix. For instance, lets say packages sort, sortuser and the main. 1) sort is compiled, the non generic part produces a *sort.a* 2) sortuser expands sort<int> so it produces *sortuser.a* and *sort_int.a* 3) main uses sort<string> and sort<int> and so it produces *main.a*, * sort_string.a* BUT sort_int.a is already available.
At link phase the program is composed of sort.a, sort_int.a (once), sort_string.a, sortuser.a and main.a
On Thursday, April 26, 2012 9:52:45 PM UTC+2, Peter Bourgon wrote:
> > Rewriting the generic code is not a bottleneck, but compiling the > generic code > > multiple times is. Also the question arises of when the code is > > compiled; if done > > at compile time, as most C++ compilers do, then if two different > > packages want to > > sort ints, the compiler has to compile the sort<int> code twice.
> If there were a C++-style, compile-time generics proposal for Go, > which somehow managed to abide "compile only once per concrete type" > semantics, would that be a "winner" from your perspective?
> > These issues may seem minor, but remember that fast compilation is a > major > > goal of the Go project, and that we want Go to be fast even when > building large > > projects. One of the reasons C++ does not have fast compilation > > is that the standard library is heavily templatized and the classes are > compiled > > many times when building a large project.
On Thu, Apr 26, 2012 at 2:05 PM, josvazg <josv...@gmail.com> wrote:
> "Rewriting the generic code is not a bottleneck, but compiling the generic
> code
> multiple times is. Also the question arises of when the code is
> compiled; if done
> at compile time, as most C++ compilers do, then if two different
> packages want to
> sort ints, the compiler has to compile the sort<int> code twice. "
> Why should be sort<int> compiled more than once?
If you define sort<T> in a package "sort" and package "heap" uses it, then
heap.a either needs to keep the code around or it has to be compiled into
it. Now, if you use it in your code and then include heap, you've got two
instances of the compiled library that are duplicates.
> When compiling the first time you cache it, the object is available
> somewhere (as a .a static library or the go compiler produced equivalent)
> to be linked by the other packages that need sort<int>.
> In fact it should be like a package appendix. For instance, lets say
> packages sort, sortuser and the main.
> 1) sort is compiled, the non generic part produces a *sort.a*
> 2) sortuser expands sort<int> so it produces *sortuser.a* and *sort_int.a*
But what if you have more than one generic thing in sort? How do you sort
a sort<int> or a sort<sort<int>>?
> 3) main uses sort<string> and sort<int> and so it produces *main.a*, *
> sort_string.a* BUT sort_int.a is already available.
> At link phase the program is composed of sort.a, sort_int.a (once),
> sort_string.a, sortuser.a and main.a
> On Thursday, April 26, 2012 9:52:45 PM UTC+2, Peter Bourgon wrote:
>> > Rewriting the generic code is not a bottleneck, but compiling the
>> generic code
>> > multiple times is. Also the question arises of when the code is
>> > compiled; if done
>> > at compile time, as most C++ compilers do, then if two different
>> > packages want to
>> > sort ints, the compiler has to compile the sort<int> code twice.
>> If there were a C++-style, compile-time generics proposal for Go,
>> which somehow managed to abide "compile only once per concrete type"
>> semantics, would that be a "winner" from your perspective?
>> > These issues may seem minor, but remember that fast compilation is a
>> major
>> > goal of the Go project, and that we want Go to be fast even when
>> building large
>> > projects. One of the reasons C++ does not have fast compilation
>> > is that the standard library is heavily templatized and the classes are
>> compiled
>> > many times when building a large project.
On Thursday, April 26, 2012 11:13:43 PM UTC+2, Kyle Lemons wrote:
> On Thu, Apr 26, 2012 at 2:05 PM, josvazg <josv...@gmail.com> wrote:
>> "Rewriting the generic code is not a bottleneck, but compiling the >> generic code >> multiple times is. Also the question arises of when the code is >> compiled; if done >> at compile time, as most C++ compilers do, then if two different >> packages want to >> sort ints, the compiler has to compile the sort<int> code twice. "
>> Why should be sort<int> compiled more than once?
> If you define sort<T> in a package "sort" and package "heap" uses it, then > heap.a either needs to keep the code around or it has to be compiled into > it. Now, if you use it in your code and then include heap, you've got two > instances of the compiled library that are duplicates.
>> When compiling the first time you cache it, the object is available >> somewhere (as a .a static library or the go compiler produced equivalent) >> to be linked by the other packages that need sort<int>.
>> In fact it should be like a package appendix. For instance, lets say >> packages sort, sortuser and the main. >> 1) sort is compiled, the non generic part produces a *sort.a* >> 2) sortuser expands sort<int> so it produces *sortuser.a* and *sort_int.a >> *
> But what if you have more than one generic thing in sort? How do you sort > a sort<int> or a sort<sort<int>>?
>> 3) main uses sort<string> and sort<int> and so it produces *main.a*, * >> sort_string.a* BUT sort_int.a is already available.
>> At link phase the program is composed of sort.a, sort_int.a (once), >> sort_string.a, sortuser.a and main.a
>> Why would it had to be different?
> It's not an easy problem.
>> Jose
>> On Thursday, April 26, 2012 9:52:45 PM UTC+2, Peter Bourgon wrote:
>>> > Rewriting the generic code is not a bottleneck, but compiling the >>> generic code >>> > multiple times is. Also the question arises of when the code is >>> > compiled; if done >>> > at compile time, as most C++ compilers do, then if two different >>> > packages want to >>> > sort ints, the compiler has to compile the sort<int> code twice.
>>> If there were a C++-style, compile-time generics proposal for Go, >>> which somehow managed to abide "compile only once per concrete type" >>> semantics, would that be a "winner" from your perspective?
>>> > These issues may seem minor, but remember that fast compilation is a >>> major >>> > goal of the Go project, and that we want Go to be fast even when >>> building large >>> > projects. One of the reasons C++ does not have fast compilation >>> > is that the standard library is heavily templatized and the classes >>> are compiled >>> > many times when building a large project.
> If we rewrite the sort package to use generics, then everybody using the sort
> package gets the slower compilation, even if they don't care about performance.
While this is true of C++ generics, it need not be true for Go if one thinks outside the box. Here's what I mean:
Assume C++-style generics were added to the Go grammar, but the compiler completely ignored the '<<T>>' annotations. The compiler would generate the same output as it currently does, in the same time (plus epsilon).
Lessons learned:
Go's code is already generic in the sense that container/list can handle any type, albeit slowly.
Dumb compilers can ignore generics.
It's not the grammar of Generics that significantly slows the compiler.
Now add Generics to the type system, but don't recompile-per-type. This doesn't dramatically slow compilation, but it gives type-safe container classes: There is no need for a type assertion when removing an int from a "List of ints", and the compiler can verify type-safety within the generic code at compile-time.
Lessons learned:
Even without recompilation-per-type, generics have advantages:
More type details can be checked at compile-time.
Run-time type assertions can be eliminated.
Now assume Go is compiled to run on a VM, and that the Generic type information is available to the VM. User-compilation speed is not slowed, but now the VM has the information it needs to recompile hotspot code at runtime. Hotspot compilation can actually create FASTER programs than static compilation since it can inline functions across modules, producing surprising performance. For example, Javascript V8 is 4x faster than C++ and 30x faster than Go on the regexdna benchmark. See also
http://wingolog.org/archives/2011/06/10/v8-is-faster-than-gcc.
Lessons learned:
Not all compilation need be at "compile time".
Compile+Run time is important, too.
Go+generics+hotspot would compile about as fast as Go.
Go+generics+hotspot could run faster than C++ for some programs.
Don't fall into the trap of adding generics implies slow compilation, or requires a complicated compiler.
I think I understand the motivation that the Go team has for fast compilation - correct me if I'm wrong - that fast compilation is an aid and comfort during development.
However, slow compilation with a resultant gain in execution time seems like a benefit if T(compilation) < nT(run), this trade off starts to benefit slow compilation with fast running code when the number of times code is compiled reduces relative to the total amount of time the code is actually running for.
So, is there a possibility (I understand that this makes the project much more complex) for two modes of compilation, a development mode where compilation is rapid, but code is not so good and a production mode where compilation time may be nearly awful, but code is excellent.
It seems to me that Russ's three options, when viewed dogmatically, are a real problem, but if use context is taken into account then it actually may give guidance about what to do.
This is probably a simplistic view, but is there some way that something like this could be achieved and would it be worth it?
On Friday, April 27, 2012 5:05:05 AM UTC+9:30, Ian Lance Taylor wrote:
> On Thu, Apr 26, 2012 at 11:31 AM, Erwin wrote:
> >> I think it's important to realize that there are different ways to > >> implement generics. In order to be able to use generics to make the > >> standard library much faster at runtime, you need to use an > >> implementation along the lines of C++ templates, in which each function > >> that uses a generic type is in effect rewritten to use that type and > >> then recompiled. This approach means that compilation slows down.
> > I don't quite get this argument of the 'slow' compilation. When thet > > programmer wants the best performance, he or she needs to implement the > > specialized functions as well, so in effect, the final amount of code > > remains the same. no? Or would the rewriting of the generic code be such > a > > bottleneck for the compiler? I can hardly imagine that.
> If we rewrite the sort package to use generics, then everybody using the > sort > package gets the slower compilation, even if they don't care about > performance. > In other words, adding generics to packages slows down compilation for > everybody, > even people who would prefer fast compilation to fast runtime performance.
> Rewriting the generic code is not a bottleneck, but compiling the generic > code > multiple times is. Also the question arises of when the code is > compiled; if done > at compile time, as most C++ compilers do, then if two different > packages want to > sort ints, the compiler has to compile the sort<int> code twice.
> These issues may seem minor, but remember that fast compilation is a major > goal of the Go project, and that we want Go to be fast even when building > large > projects. One of the reasons C++ does not have fast compilation > is that the standard library is heavily templatized and the classes are > compiled > many times when building a large project.
> I think I understand the motivation that the Go team has for fast
> compilation - correct me if I'm wrong - that fast compilation is an aid and
> comfort during development.
> However, slow compilation with a resultant gain in execution time seems
> like a benefit if T(compilation) < nT(run), this trade off starts to
> benefit slow compilation with fast running code when the number of times
> code is compiled reduces relative to the total amount of time the code is
> actually running for.
> So, is there a possibility (I understand that this makes the project much
> more complex) for two modes of compilation, a development mode where
> compilation is rapid, but code is not so good and a production mode where
> compilation time may be nearly awful, but code is excellent.
> It seems to me that Russ's three options, when viewed dogmatically, are a
> real problem, but if use context is taken into account then it actually may
> give guidance about what to do.
> This is probably a simplistic view, but is there some way that something
> like this could be achieved and would it be worth it?
> Dan
> On Friday, April 27, 2012 5:05:05 AM UTC+9:30, Ian Lance Taylor wrote:
>> On Thu, Apr 26, 2012 at 11:31 AM, Erwin wrote:
>> >> I think it's important to realize that there are different ways to
>> >> implement generics. In order to be able to use generics to make the
>> >> standard library much faster at runtime, you need to use an
>> >> implementation along the lines of C++ templates, in which each
>> function
>> >> that uses a generic type is in effect rewritten to use that type and
>> >> then recompiled. This approach means that compilation slows down.
>> > I don't quite get this argument of the 'slow' compilation. When thet
>> > programmer wants the best performance, he or she needs to implement the
>> > specialized functions as well, so in effect, the final amount of code
>> > remains the same. no? Or would the rewriting of the generic code be
>> such a
>> > bottleneck for the compiler? I can hardly imagine that.
>> If we rewrite the sort package to use generics, then everybody using the
>> sort
>> package gets the slower compilation, even if they don't care about
>> performance.
>> In other words, adding generics to packages slows down compilation for
>> everybody,
>> even people who would prefer fast compilation to fast runtime
>> performance.
>> Rewriting the generic code is not a bottleneck, but compiling the generic
>> code
>> multiple times is. Also the question arises of when the code is
>> compiled; if done
>> at compile time, as most C++ compilers do, then if two different
>> packages want to
>> sort ints, the compiler has to compile the sort<int> code twice.
>> These issues may seem minor, but remember that fast compilation is a
>> major
>> goal of the Go project, and that we want Go to be fast even when building
>> large
>> projects. One of the reasons C++ does not have fast compilation
>> is that the standard library is heavily templatized and the classes are
>> compiled
>> many times when building a large project.
On Thu, 2012-04-26 at 17:38 -0700, Paul Borman wrote:
> That is why there is gccgo, to improve execution time. Use the fast
> tools to develop and the slow tools to make the production version.
dan.kortsc...@adelaide.edu.au> wrote:
> Yeah, I was including that in my thinking, but was wondering if that
> notion could be extended further.
> thanks
> Dan
> On Thu, 2012-04-26 at 17:38 -0700, Paul Borman wrote:
> > That is why there is gccgo, to improve execution time. Use the fast
> > tools to develop and the slow tools to make the production version.
> > -Paul
-- Michael T. Jones | Chief Technology Advocate | m...@google.com | +1
650-335-5765
> josvazg <josv...@gmail.com> writes:
> > So, as a summary, due to the lack of "a generic like solution" right now
> > some parts of the Go standard library might be less performant than they
> > could be, even though the algorithms they use are solid, cause it's TOO
> > much work to repeat yourself for each and every primitive type and make it
> > an ah-hoc solution for those.
> I think it's important to realize that there are different ways to
> implement generics. In order to be able to use generics to make the
> standard library much faster at runtime, you need to use an
> implementation along the lines of C++ templates, in which each function
> that uses a generic type is in effect rewritten to use that type and
> then recompiled. This approach means that compilation slows down.
> A different approach would be to, in effect, use the reflection
> interface in the generic code. That would give you the programming
> convenience, and keep the fast compilation times, without any large
> runtime benefit. This approach is closer to (although certainly not the
> same as) the way that C# or Java implement generics.
Peter Bourgon <pe...@bourgon.org> writes:
>> Rewriting the generic code is not a bottleneck, but compiling the generic code
>> multiple times is. Also the question arises of when the code is
>> compiled; if done
>> at compile time, as most C++ compilers do, then if two different
>> packages want to
>> sort ints, the compiler has to compile the sort<int> code twice.
> If there were a C++-style, compile-time generics proposal for Go,
> which somehow managed to abide "compile only once per concrete type"
> semantics, would that be a "winner" from your perspective?
It's hard to answer that sort of question. The exact details matter.
And of course I should add that now that Go 1 has been released we are
not going to change the language for some time even if we had a perfect
proposal.
josvazg <josv...@gmail.com> writes:
> Maybe it would make sense to forbid "recursive generics" instead of > generics altogether?
> (Recursive generics look awful anyway)
We absolutely want to be able to have a list of list of int.
Glenn Brown <tornadogl...@gmail.com> writes:
> Assume C++-style generics were added to the Go grammar, but the
> compiler completely ignored the '<<T>>' annotations.
I guess I'm not sure what that means.
> Now add Generics to the type system, but don't recompile-per-type.
This is not as easy in Go as it is in some languages, because types in
Go are not all the same size. E.g., since we've been talking about
sort, sort on []int8 is a different operation from sort on []float64,
not just because you need to use different comparison routines, but
because you need to use different indexing operations.
"All other generic usages can be separated one expansion into one .a at a time, can't they?"
I guess they can't, cause when you use a package you get it all, you can't pick to get ONLY what you really use of it.
That means that, for a big project linking with many packages that use generic expansions will pull ALL the generic expansions on imported packages even if they are not used by the final program.
And this will have no solution that DOESN'T make the compiler slower because making the packages to be able to subdivide themselves into actual used and unused parts also costs time and means recursion.
On Thursday, April 26, 2012 7:49:18 AM UTC-10, Glenn Brown wrote: > For example, one could allow a special type '_' in interface declarations > to mean "the same type as the method's receiver" and then declare > interfaces like this: > type Lesser interface { > Less(_) bool > }
That looks like the sweetspot 80-20 solution to me. It lets you do performant container types without getting into the C++ brain busting meta-programming. The fact is almost all of what people want generic programming for can be done at runtime using reflection. So, the real question is how much syntactic support should be added to make common tasks easier and where we can get performance wins without sacrificing compile time. Getting rid of int --> interface{} --> int does it for me.
>> Assume C++-style generics were added to the Go grammar, but the
>> compiler completely ignored the '<<T>>' annotations.
> I guess I'm not sure what that means.
Here's a concrete example I used at http://gosailthec.blogspot.com/ , where '~Int' means 'of Int' and 'default T interface{}' means 'T defaults to interface, but may be any type implementing that interface.' It is a generic version of 'List'.
default T interface{}
type Element~T struct {
next, prev *Element~T
list *List~T
Value T
}
type List~T struct {
front, back *Element~T
len int
...
func (l *List~T) PushFront(value T) *Element~T {
e := &Element~T{nil, nil, l, value}
l.insertFront(e)
return e
}
...
In that example, if you replace 'default' with 'type' and remove all the '~T' annotations, then you effectively return to the original container/list code, which can hold any type. The code already works for all T that satisfy interface{}. In this sense, the Go container is already generic and can be used for all T. Adding the "~T" annotations (or "~(T1,T2,…)" in general) simply provides additional type checking information, so the compiler can enforce type-preservation within the compilation unit, can verify that callers insert consistent types, and can know that consistent types are returned.
Other than this additional type checking, the compiler is free to ignore the '~' annotations and emit code for 'type T interface{}'. This is what the current Go compilers do, and it generic enough for the first pass at compilation.
>> Now add Generics to the type system, but don't recompile-per-type.
> This is not as easy in Go as it is in some languages, because types in
> Go are not all the same size. E.g., since we've been talking about
> sort, sort on []int8 is a different operation from sort on []float64,
> not just because you need to use different comparison routines, but
> because you need to use different indexing operations.
Yes, I've been focusing so much on non-array containers that I have neglected this issue when thinking about array sorting, and would need something like
type Array interface {
Get (index int) interface{}
Put (index int, interface{})
}
to use a.Less(b) for comparison in array sorting. Not good.
But please note that the 'Less(a,b int) bool' approach used by sort does not generalize to containers that don't use ordinal addressing, such as priority queues, trees, skip-lists, graphs… For these, it is more natural to use 'interface { Less(other interface) bool }' described by others or the 'interface { Less(_) bool }' approach described elsewhere in this thread and the blog.
I've been thinking that generics could be also implemented by specializing on interfaces.
For example:
type Comparable interface{ Less(Comparable) bool }
func Min(a Comparable, b Comparable) c Comparable { if a.Less(b){ return a
} return b }
Min`{Comparable int}(3,4) // which would be same as implementing function Min with all Comparable-s replaced with int. // of course you would need to be able to say: MinInt := Min`{Comparable int} // or maybe? func MinInt Min`{Comparable int}
generic compose function type T interface{} func Compose(a func(T)T, b func(T)T) func(T)T { return func(x T)R{ return b(a(x)) }
}
Compose`{T int}
// this idea could easily work with structs (or maybe even packages):
type N interface{ Compare(b N) int
}
type BinaryTree struct{ Left *BinaryTree Right *BinaryTree Value N
}
type MyValue struct { V int } type MyTree struct { tree BinaryTree`{N MyValue} // alternatively BinaryTree`{N MyValue, BinaryTree MyTree}
}
// essentially says that N should be replaced with MyValue and BinaryTree with MyTree // also all methods declared on BinaryTree would need to be interface specialized as well
This seems to make sense because you first need to implement everything so that it works on a specific interface and then specialize. So if your code works with general interfaces it must therefore work with specialized types. Also this seems to fit nicely with interfaces.
On Monday, November 21, 2011 11:44:39 PM UTC+2, Miki Tebeka wrote:
> Greetings,
> Maybe we don't need generics. Maybe the solution is enhancing interfaces to > cover basic types.
> This idea came to me while going over http://learnyouahaskell.com. There > if you > want to have a generic "max" function, it takes "Ord" typeclass > (interface). If > we'll have Go numbers also implement "Ord" interface then a generic "max" > will > be easy to write.
Glenn Brown <tornadogl...@gmail.com> writes:
> In that example, if you replace 'default' with 'type' and remove all
> the '~T' annotations, then you effectively return to the original
> container/list code, which can hold any type. The code already works
> for all T that satisfy interface{}. In this sense, the Go container
> is already generic and can be used for all T. Adding the "~T"
> annotations (or "~(T1,T2,…)" in general) simply provides additional
> type checking information, so the compiler can enforce
> type-preservation within the compilation unit, can verify that callers
> insert consistent types, and can know that consistent types are
> returned.
> Other than this additional type checking, the compiler is free to
> ignore the '~' annotations and emit code for 'type T interface{}'.
> This is what the current Go compilers do, and it generic enough for
> the first pass at compilation.
Thanks for the explanation. To me that approach sounds very much like
Java's type erasure approach. I think that approach has a serious
problem: when I pass List~int to a function that takes interface{}, I've
lose the type information. There is nothing preventing me from doing a
type assertion to List~float64; I do get the runtime checks when I try
to pull the values out, but I don't know the type of the container.
> Thanks for the explanation. To me that approach sounds very much like
> Java's type erasure approach. I think that approach has a serious
> problem: when I pass List~int to a function that takes interface{}, I've
> lose the type information. There is nothing preventing me from doing a
> type assertion to List~float64; I do get the runtime checks when I try
> to pull the values out, but I don't know the type of the container.
> Ian
Thanks for the feedback.
I'm afraid I don't understand how this problem is specific to the '~T' approach: when you pass *anything* to func(interface{}) you lose the type information, except that you can recover it through reflection/inference plus type assertions. What are you hoping would be handled better?
Ironically, my main motivation for thinking about ~T and the '_' type was annoyance at containers based on interface{} discarding type information! If there is anywhere type information can be preserved without recompilation, it is for containers.
> > Thanks for the explanation. To me that approach sounds very much like
> > Java's type erasure approach. I think that approach has a serious
> > problem: when I pass List~int to a function that takes interface{}, I've
> > lose the type information. There is nothing preventing me from doing a
> > type assertion to List~float64; I do get the runtime checks when I try
> > to pull the values out, but I don't know the type of the container.
> > Ian
> Thanks for the feedback.
> I'm afraid I don't understand how this problem is specific to the '~T'
> approach: when you pass *anything* to func(interface{}) you lose the type
> information, except that you can recover it through reflection/inference
> plus type assertions. What are you hoping would be handled better?
One thing that's nice about a []int is that you can type assert to it. If
you have a List<int> and pass it to an interface{}, with type erasure it is
simply a List at runtime and you cannot, for instance, distinguish easily
between what used to be a List<int> and List<string>.
> Ironically, my main motivation for thinking about ~T and the '_' type was
> annoyance at containers based on interface{} discarding type information!
> If there is anywhere type information can be preserved without
> recompilation, it is for containers.
> One thing that's nice about a []int is that you can type assert to it. If you have a List<int> and pass it to an interface{}, with type erasure it is simply a List at runtime and you cannot, for instance, distinguish easily between what used to be a List<int> and List<string>.
But you can! An interface is a pair of pointers, one for data and one for metadata. The runtime simply needs a different metadata pointer for each constructed type. Then, List<int> and List<string> are distinct types. In other words, just because one doesn't use C++ code recompilation doesn't mean one can't have distinct types that use the same machine instructions behind the scenes, but different metadata.
> On Apr 27, 2012, at 11:06 AM, Kyle Lemons wrote:
> One thing that's nice about a []int is that you can type assert to it. If
> you have a List<int> and pass it to an interface{}, with type erasure it is
> simply a List at runtime and you cannot, for instance, distinguish easily
> between what used to be a List<int> and List<string>.
> But you can! An interface is a pair of pointers, one for data and one for
> metadata. The runtime simply needs a different metadata pointer for each
> constructed type. Then, List<int> and List<string> are distinct types. In
> other words, just because one doesn't use C++ code recompilation doesn't
> mean one can't have distinct types that use the same machine instructions
> behind the scenes, but different metadata.
Isn't that contrary to the purpose of type erasure? And they can't have
the same instructions if it's type List~T []T.