I've implemented a much nicer version of my gotit templating
implementation of generics for go, which you can grab at:
http://github.com/droundy/gotit
The syntax is much improved (I now use go.scanner), so a template
looks something like:
package slice(type a)
func Append(s *[]a, val a) {
l = len(*a)
if l == cap(*a) { ... }
*a = (*a)[0:l+1]
(*a)[l] = val
}
You can see that the package declaration just has a list of type
parameters added. There's a bit more complexity, in that you need to
specify a "default type" if your code relies on some particular sort
of type (e.g. an interface). So for instance, if you write a package
like
package mymath(type a int)
func max(x, y a) { if x > y { return x }; return y }
the package will only be able to be instantiated with types that are
assignment-compatible with int. If you left off the int default type,
then the template would fail to compile, because > is not defined for
the interface{} type.
Which brings us to how to actually instantiate a template. You use a
template by importing it, as in:
import int32math "mymath(int32)"
There is a helper program "gotimports", which will parse your go file,
and instantiate any templates it finds imported, provided the
templates are in your local path. Searching for templates installed
in a central location is still on the TODO list. You can also use the
"gotit" program to generate templated go files directly, which is just
a bit more work.
I'll appreciate any feedback! I think this is quite an exciting
approach for generics, and should very useful.
--
David Roundy
Based on a couple of suggestions, I've renamed gotit to gotgo, so the new URL is
http://github.com/droundy/gotit
David
Perhaps such a preprocessor would also be nice
for other language features that features that perhaps won't make it
into Go proper? Like operator overloading, perhaps? :)
We'll be C++ feature complete in no time!
-jessta
--
=====================
http://jessta.id.au
Any serious large-scale project will invariably create a rich library
of data types and related code that is fairly domain-specific (i.e.,
don't rely on someone else writing it for you). For example, a math
library will spawn a variety of types such as vector, matrix,
quaternion, etc., and the first question that will come up is how to
handle the three different float types and ideally the 11 different
integer types. Older languages are stuck with replicating every single
data structure, and you will indeed see just this kind of mess in
libraries written for C. Some of the more innovative library authors
will wrap entire headers and source files in macros that substitute
some "T" for the appropriate type, thus essentially emulating
generics. It would be disappointing to see Go end up in the same
pickle, and the Vector library hints that this is precisely where it's
headed.
Java may have achieved considerable success without generics, but
after experiencing the pain of programming with pre-generics Java, I
can well understand why they finally capitulated. Java is a perfect
example of why Go _should_ get generics. Of course, Java's generics
were done quite badly[1], so firstly, extreme caution is in order when
thinking about Go's generics and secondly, the best time to do it is
when the language design is still fluid. This is why I very much like
the idea of a preprocessor-based implementation to serve as a test-
bed; with minimal effort, it lets the community sift through a wide
range of ideas, discarding a lot of bad ones in the process.
[1]The version of the story that I am familiar with: When implementing
Java generics, the designers tried very hard to avoid changing the JVM
and bytecode format. This lead to the introduction of the now infamous
type-erasure semantics. Apparently the bytecode ended up requiring
changes anyway, but it was too late in the process to revisit the
whole design, so they were stuck with an ugly compromise that proved
unnecessary. Generics were nonetheless a big improvement to Java, in
spite of the compromises.
Whether Java's generics were really an improvement is debatable.
You still need conversions in Java, and Java's generics don't really improve efficiency, so what is really improved?
Clarity. Anywhere one uses a container or an iterator, which is to say, all over the place.
Clarity. Anywhere one uses a container or an iterator, which is to say, all over the place.I'm not convinced generics make anything clearer.GenericContainer <Float> myContainer = new GenericContainer <Float> ();
for (float v : myContainer) {...}Compare to:Container myFloatContainer = new Container ();for (float v : myFloatContainer) {...}I don't think there is much of a difference.
Notice that the iterator syntactic sugar in both examples has nothing to do with generics.
Also notice that I can abuse the container in either case, and that they are no different after erasure.
Whether Java's generics were really an improvement is debatable.
I think there are two main roads a language can follow. First: template everything. See STL and Boost. Second: support only a few built-in data types in your libraries. See Go's math, Vector, etc.I've written enough C++ to know that templating everything can be hell. Maybe you save a few instruction cycles in the end, maybe even a few hundred milliseconds somewhere, but is it really worth writing STL-style code every time you want a new container? Have you ever tried to extend STL or Boost? Not a fun time.
Java, for legacy reasons, takes the middle road: a lot of packages are implemented with generics, but a lot aren't. It is easy to find methods in common packages that accept a parameter of type, say, float[], and you must convert your generic <Float> container back into an array to use it, etc. While generics were meant to avoid conversions like these, a half-baked approach is inefficient and a pain.Therefore, I think Go's current approach isn't horrible. You might need to convert a []float to a []float64 once in a while, but the same can be said of Java. You still need conversions in Java, and Java's generics don't really improve efficiency, so what is really improved? I'd rather deal with conversions (which are simple, well-defined) than with STL-like templates all over the place.
That's why, rather than arguing abstractly, implementations such as
David's preprocessor are very important. They let us test the feature.
So in stead of "generic criticism", if you pardon my pun, I'd rather
see
criticism on David's implementation, how it could be improved, what
it
is missing, and what you like and dislike about it. That's the way
forward.
If you want to see a well-designed generics architecture, have a look at .Net generics.
But it hardly constitutes an argument against generics per se.
Typically I haven't had to abuse containers.
the checking makes it much easier to get this right
If you want to see a well-designed generics architecture, have a look at .Net generics.I have used .NET extensively, but I'm not impressed. Improving a bit over Java isn't saying much.
But it hardly constitutes an argument against generics per se.Sorry if I'm pessimistic here. .NET languages suffer from a commercial mentality wherein MS has tried to please everyone (including yourself) by throwing in every feature imaginable. It is still easier than C++ (shocking) and feels more powerful than Java (amazing) but isn't particularly better than either,
if you ask me. .NET certainly fails by _my_ standard of excellence, which is only simplicity.
If you have a revolutionary approach to generic code, I'd be very interested to hear about it at POPL or something. I'm just saying that I _have_ used generics in many languages, and the languages I actually use do not support them, and I don't often miss them.
I wouldn't be horribly upset if Go supported generics at some point, but my experience with Go so far has shown me that generics aren't altogether orthogonal to interfaces.
Thanks
Hi,
I'm new go user here my 2cents about generics.
Generics is about introducing type operator so that one get a new type
by combining 2 other types, like Vector<Car> is the result of Vector
and Car.
Having type operator will lead to nesting construct like
Type1<Type2<Type3...>>>.
While mere mortal developers will not use
that construct that often,
the language implementator will have to get
it right. That will certainly complicate the type checker
and a consequence, slowdown the compiler.
One goal of go is "it compiles fast". To achieve that, the language has to be relative simple.
Thanks!
Y.
Sorry I don't understand why you are seeing this as a new type. All I would read is that Vector<Car> is
only allowed to hold type Car, perhaps you can explain your reasoning?
Thanks!
That'd be because programmers don't have to write code to handle the unboxing. A large part of the appeal of dynamic languages is that they don't clutter code with declarations, type casts or pointer maths. They also don't need generics because they focus on runtime state rather than compile-time assumptions about that state.
> I agree that Go will not fit some niches without generics, but those niches don't happen to overlap with mine.
Then your niche is far removed from the mainstream. Go could choose to be a ghetto, helping a few people solve particular problems where concurrency is the overriding consideration, or it embraces the needs of mainstream developers - many of whom work on largish (100k+ line) programs and rely on tools such as generics to simplify and reduce that codebase to a more maintainable level of complexity.
Personally I'm not particularly keen on generics but they're certainly a functional tool that's helped simplify a lot of C++ and Java code, and that's a definite win for developers working in those languages.
I'd much rather have concrete interfaces capable of supporting methods. For types such as Vector this would allow a lot of the drudgery involved in implementing type-specific variants to be factored out into a single core implementation as well as reducing the amount of explicit boxing and unboxing necessary.
In either case Vector is a type in its own right regardless of its contents and having a way of concretely expressing that fact is a good thing.
Ellie
Eleanor McHugh
Games With Brains
http://feyeleanor.tel
----
raise ArgumentError unless @reality.responds_to? :reason
Can you express it more conveniently in Go, using interfaces?
Can you express it more conveniently in Go, using interfaces?See package "sort".
func Sort(data Interface)Is that a less convenient interface?
Finally, none of this extra implementation effort is at all convenient if you want to sort an array containing any of the other 13 numeric types (or use your own comparator).
sort.Interface doesn't offer the primitives required to implement merge-sort
// A dirList implements sort.Interface.
type dirList []*os.Dir
func (d dirList) Len() int { return len(d) }
func (d dirList) Less(i, j int) bool { return d[i].Name < d[j].Name }
func (d dirList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
There's absolutely no requirement that the input fit in RAM here.
You can implement those three functions however you see fit.
The SortInts, SortFloats, etc., are just helpers for a couple of
common types. But it's a lot more common to have your own
type, as for *os.Dir here, or to substitute a different comparison,
and so on.
Is four lines of code too much?
Russ
This convinces me that this discussion we're having here may be more
or less academic. I think that the best way to convince people of the
usefulness of templates is not by arguing about it here, but by
actually /using/ and /improving/ tools like gotgo. Let's create a
whole bunch of wonderful Go generics packages that use gotgo to
demonstrate the usefulness of generics in GO. The proof will be in the
pudding. :)
Kind Regards,
B.
Can you express it more conveniently in Go, using interfaces?See package "sort".
func Sort(data Interface)Is that a less convenient interface?
Finally, none of this extra implementation effort is at all convenient if you want to sort an array containing any of the other 13 numeric types (or use your own comparator).That's exactly what the interface is for. You can sort any of your 13 numeric types, or any other type with the sort package already.
sort.Interface doesn't offer the primitives required to implement merge-sortIf you need a merge-sort specific interface, make one.Again, I encourage everyone to try implementing generic code in Go using interfaces before you insist that generics are necessary.
It is possible, simple, and only slightly redundant (if you want to avoid boxing), as illustrated already in the standard library.
Have you looked at the uses of sort in the tree?
The amount of code you need to write to sort a new
data type is very small. Here's the code from
src/pkg/io/ioutil.go that sorts directories read from
an *os.File:
// A dirList implements sort.Interface.
type dirList []*os.Dir
func (d dirList) Len() int { return len(d) }
func (d dirList) Less(i, j int) bool { return d[i].Name < d[j].Name }
func (d dirList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
There's absolutely no requirement that the input fit in RAM here.
You can implement those three functions however you see fit.
The SortInts, SortFloats, etc., are just helpers for a couple of
common types. But it's a lot more common to have your own
type, as for *os.Dir here, or to substitute a different comparison,
and so on.
Is four lines of code too much?
I'm not sure, but things like that are done with reflection?
D.
in the absence of generics, the current definition of sort.Interface
supports the sorting of unboxed arrays of objects.
it's easy to implement something similar to the C# approach
if you're willing to pay the boxing/unboxing penalty.
see the attached code for an example of how this can be done.
If you have:
package foo(type a);
Can you do:
package bar(type b);
import foo "./foo(b)"
On Feb 27, 7:28 am, David Roundy <roun...@physics.oregonstate.edu>
wrote:
> Hi all,
>
> Based on a couple of suggestions, I've renamed gotit to gotgo, so the new URL is
>
> http://github.com/droundy/gotit
>
> David
actually, that was (almost) the signature that i originally gave FuncSort,
but i changed it to return a sort.Interface because that means that it's
compatible with any other function that expects a sort.Interface
(e.g. sort.IsSorted). the type casting is necessary without generics.
> this is unavoidable without generics).
yes, go does not (yet?) have generics. this has been much discussed,
and complaining about it won't make it happen any faster, i'm afraid.
yes, go does not (yet?) have generics. this has been much discussed,
and complaining about it won't make it happen any faster, i'm afraid.
That's a good question. That *should* work, but it isn't something
I've gotten around to testing, so it probably doesn't. I've been
quietly hacking away on gotgo, and recently stopped to spend some time
switching my other projects to use gotgo, but I haven't gotten around
to making sure that works.
It's not so very hard to do, it just means that gotgo is going to have
to parse the import statements looking for references to the type
parameters. I recently replaced gotimports with a new gotmake program
that does the same job, but generates a makefile rather than running
gotgo itself. You can then either use the makefile by hand or use it
automatically (which is what I'm doing).
In the process of this rewrite (and getting things working in
iolaus-go and goopt), I changed to actually use the go parser to parse
the import strings when looking for imports needed to define the type.
e.g. so importing "./gotgo/slice([]foo.Bar)" will ensure that package
foo is available to the generated go file. So I already have code to
parse those strings, and would just need to put it into gotgo itself,
and we'd have your feature (which really is an important one).
David
--
David Roundy