On Mon, May 17, 2010 at 12:32 AM, german diago <
germa...@gmail.com> wrote:
> I think the approach is quite sensible regarding to the power they
> give you, but I don't know how difficult to implement my requirements
> are, namely:
>
> - alias types.
Gut feeling: Not too hard.
> - constraints on template parameters.
This will open a can of worms to battle. If you look at Javas
generics, they have a notion of a /bounded wildcard/ which allows you
to say List<? extends Foo>. This signifies an /unknown/ type extending
the Foo type. In Go, that would be extension of the Foo interface. Its
dual, List<? super Foo>, signifies an /unknown/ supertype of Foo. The
typing rules for these two constructions are somewhat unintuitive. The
reason for all this hoop-jumping is a peculiar thing of subtyping with
generics. If Bar extends Foo then it is /not/ the case that List<Bar>
extends List<Foo>!
In practice, the system of Java is derived from a system named F-sub,
System-F with bounded quantification. But it has to be restricted
since full F-sub type checking is undecideable (Piece, 1993 if memory
serves). Luca Cardelli was also deeply involved in F-sub IIRC.
> - interface templates
> - struct templates
> - type templates
Templates tend to avoid the problem by expansion at compile time.
Whenever you need a new template type, expand code with substitution.
This is somewhat dangerous as it tend to lead to the path of utter
despair and sheer horror, as Serge already gave examples of.
The other path to take is the naive one: Add parameters to types, but
do not allow one to use that type for anything "inside" the container.
Thus all the container has is a pointer to the type, with no
recollection of what type it is sitting with. Subtyping hierarchies
with template types are outlawed totally due to the above problem of
List<Bar>/List<Foo>. Gut feeling: Solves most real-world problems
effectively, is somewhat simple to implement. One has to be extremely
cautious when messing with type systems however. It is very easy to
mess them up so they loose nice properties. I think this idea rather
closely matches the 'map'-construction in Go.
Another idea I had a Go at, was package parametrization. The idea
stems from the ML module system. You allow to parameterize a complete
package on another package. In Go, One would perhaps opt for a simpler
variant where you can parameterize a package on one or more
interfaces. It allows the code inside the package to use the
interface-methods only when dealing with the interface type. In an
import statement, one can supply the needed interface and name:
import (
"foo"<int> as fooint // requires syntax improvement, but the idea is there
"foo"<string> as foostring
)
Compilation however, is not that simple. The above will require two
compilations of foo, one for each type. Furthermore, we will only
learn about this need when compiling the package containing the
import. If it *is* already compiled and available on disk however, we
can skip recompilation, which somewhat improves the situation. Another
even more sinister low-level path to take is to abort compilation of
the package if the prerequisites are not there. Thus, compilation
dependencies are outsourced to either the programmer, or a tool which
scans source code and builds dependency DAGs and lays them out with a
topological sorter.
The grand caveat with generics: They are hard in subtyping
environments. They come back to haunt and bite you and will happily
sink their fangs into the unsuspecting new user.
--
J.