I am in the middle of implementing a generic tree type as part of a CLI
flags/configuration package, and a key goal in my design is to make the
declarations it parses as easy to read as possible, and as far as possible,
avoiding too much boilerplate.
Yes, nothing new there, but in the process, I have noticed something that
isn't at the top of people's minds when they think of generic data types:
validation
Actually, default values is another aspect of generics as well, because a
generic type also has to have some kind of valid new/empty state, to stay
consistent with the way that the builtins are always zeroed before use, but
I'll address the former first.
Well, ok, let's just say that implementation of generic structures, (which
implicitly aren't very generic in implementation), works with the
interface{} container, which is a very limited Set structure, with one
implicit function that when satisfied makes the variable it encapsulates
recognised as a complete implementation and permits interchange with other
implementations' type.
So, in Go, this means (and same in all languages though they sugar coat it
in various automatic stuff) that before you can call methods on a generic
type, the contents of the variable have to be checked.
>From where I am at right now, this is of course my job as the programmer to
write these. However, my reason for all the rambling up to this point is
that I think that since validation is mandatory for dynamic types, that the
proposed generics syntaxes that are under consideration should include
constraint/contract type validation. For this, then there must be a
constructor and a bounds/validation function that ensures that its methods
will find everything that must be there, where it must be, preferably
before the main() is invoked.
On the latter subject, well, I think I already covered that - just simply
that when you declare a new type, all zeroed may not actually be a valid
new and empty variable. The specifics of a type may mean that certain
things must be in certain places, and the things that are in the container
are valid members of the set of possible contents and that these members
contents are not outside of the bounds that would lead to bugs.
It does lead me to a thought about a go-idiomatic style of generic type,
however - and it all centers around the type declaration syntax. Not all
generics are going to need something other than the standard zeroing, so
the initialiser function need not be mandatory. This is starting to blend
in an unseemly way into some parts of OOP with the concept of a constructor
(go dodges the need for destructors mostly, but I would suppose in
implementation a destructor might be a good idea especially for resources
that need freeing manually).
Actually, right there might be another issue that might be important with
generics, but more generally - if there could be some way to tag a type
such that unless you specify otherwise, it runs its closer on implicitly on
close of the function scope it lives in, and this also suggests that some
means of indicating this relationship between two methods would also make
sense.
So, taking stock, I am saying that a consistent Go styled generic would
need to have builtins for initialisation to valid empty state, one for
freeing resources locked up during lifecycle, and some way of specifying a
valid range off values for the type.
When you really look at it, implementing generics basically means code
generation involving a lot of reflection, or, the likely and better option,
that compilation creates tagged sets for every type defined in a package,
so that these attributes are available without the expensive parsing of the
metadata tied up in the symbol table. If my surmise is correct, it means
that creating a complete generics syntax for Go also means creating
constructors, destructors, validation/sanitising, and adding the necessary
metadata to be efficiently accessible at runtime instead of parsed out of
linker metadata.
It also brings up the issue that there is a more than subtle connection
between generic types and exception/error handling, with regard to
validation, the construct required for this is structurally similar to
error handling, in that, already I have described initialisation,
deallocation, and validation, all of these are states, and in
implementation it means that one should consider carefully whether there is
other things that seem at first blush, peripheral, but should be considered
indispensible...
Namely, the error value.
I thought a lot about this in the last few weeks, and I had already in
practise experimented with creating pipeline-pattern types using
interfaces, in which the error was inside the struct containing the
variable data (byte slice in this case), and the interface included several
methods relating to the examination, setting and resetting of the error
value. I didn't quite get to fully codifying in my mind exactly what was
mandatory and what you will regret not putting in there (to prevent edge
cases and potential injections).
Think about the situation with the basic, machine-level built-in types. Do
ints and floats have an error value? Superficially, one might say 'no'
based on what is specified in the Go language definition. But this is
entirely incorrect. These types are implemented mostly 100% directly in the
CPU, and every operation involving them can affect the Condition Code
register. Normally this state information is transparently part of the
various comparison operators, but I can see how it would make a lot of
sense if you could interrogate these variables and get some representation
of the CC, and that this metadata would have to be part of any error
handling scheme added to the language.
And then, if we expose this data in these base types, logically the
metadata for errors in compound, especially generic types, has a further
set of type-specific states. This also could help a lot with both error and
generics handling, for example, one of the flags could indicate that the
last thing done to the variable was being zeroed. For some purposes,
receiving an unmodified fresh variable could be written in as a constraint
in the function header, so you would have !nil and !zero constraints on a
generic method parameter, and, well, I'll just finish on this last point -
a lot of error handling becomes irrelevant when you can concisely specify
constraints on a parameter's values and states, that cuts it off at the
pass.
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.