type T struct {
a int const;
b type const;
c [a]int;
}
func d(e b) { f[a] }
Const fields could be initialized explicitly for a type, doing all
kinds of custom checks there. This also sets up address tables for
method calls and calculates function stack sizes for local arrays.
Then this immutable partial struct is used in initialization of
instances as first argument z:=T{!200, int}; y:=T{z, ...}.
In such case, complexity of syntax grows minimally, but templates are possible.
--
Tambet
type collection struct {
const t type;
const size int;
values [size]T;
}
type collection2 struct {
const t type;
const size int;
keys [size]string;
values [size]T;
}
// Only methods with “const” can be called on uninitialized
collection,
// thus it's not a subject of “Collection” interface.
func (c *collection const) NewCollection() *collection {
return collection{c, ...}
}
func (c *collection2 const) NewCollection() *collection {
return collection2{c, ...}
}
type CollectionFactoryInterface interface {
NewCollection();
}
func NewCollectionType(t type, size int, hasKeys bool)
*CollectionFactoryInterface {
// Check t with reflection for compability
if size < 0 {
// Runtime would panic anyway
// after calculating size of “values”
panic(“Collection too small”)
}
if hasKeys {
return collection2!{t, size}
} else {
return collection!{t, size}
}
}
func (c *collection) GetValue(i int) *t {
return collection{c, ...}
}
func (c *collection2) GetValue(k string) *t {
return collection2{c, ...}
}
func (c *collection) GetType() type {
}
Those should return obviously *CollectionInterface
const IndexedCollection collection!{int, 200}
const CollectionWithKeys collection2!{int, 200}
Because when you are declaring those structs, you know, which specific
version you are going to use (so compiler wont run initializer):
const IntCollection collectionOptimizedForIntegers!{} // Non-generic
type with NewCollection const method
const StringCollection collection!{string}
const BoolCollection collection!{boolean}