Constraint with slice and map union: invalid operation: cannot index c (variable of type T constrained by sliceOrMap) [duplicate] - go

I decided to dive into Go since 1.18 introduced generics. I want to implement an algorithm that only accepts sequential types — arrays, slice, maps, strings, but I'm not able to crack how.
Is there a method that can be targeted involving indexability?

You can use a constraint with a union, however the only meaningful one you can have is:
type Indexable interface {
~[]byte | ~string
}
func GetAt[T Indexable](v T, i int) byte {
return v[i]
}
And that's all, for the time being. Why?
The operations allowed on types with union constraint are only those allowed for all types in the constraint type set.
To allow indexing, the types in the union must have equal key type and equal element type.
The type parameter proposal suggests that map[int]T could be used in a union with []T, however this has been disallowed. The specs now mention this in Index expressions: "If there is a map type in the type set of P, all types in that type set must be map types, and the respective key types must be all identical".
For arrays, the length is part of the type, so a union would have to specify all possible lengths you want to handle, e.g. [1]T | [2]T etc. Quite impractical, and prone to out-of-bounds issues (There's a proposal to improve this).
So the only union with diverse types that supports indexing appears to be []byte | string (possibly approximated ~). Since byte is an alias of uint8, you can also instantiate with []uint8.
Other than that, there's no other way to define a constraint that supports indexing on all possible indexable types.
NOTE that []byte | string supports indexing but not range, because this union doesn't have a core type.
Playground: https://gotipplay.golang.org/p/uatvtMo_mrZ

Related

How to check two structs for equality

I have two instances of this struct with references inside (as properties):
type ST struct {
some *float64
createdAt *time.Time
}
How can I preform a check for equality for two different instances of this struct? Is it only by using reflect?
While you could use reflection, as Corey Ogburn suggested, I would not do so for a simple struct like that. Per the official Go Blog, reflection is
a powerful tool that should be used with care and avoided unless strictly necessary
-- The Laws of Reflection
It should be a simple exercise for you to write a function that takes two pointers to values of your struct type and returns a boolean true/false as to whether they are equal, first by testing for nil pointers and then by testing for equality of each of the fields of the struct.
time.Time values already have an equality test method with signature
func (t Time) Equal(u Time) bool
Depending on your use cases, the bigger problem may be comparing two floating point values for equality. While == comparisons work on float64 values, for many applications you want two float values to be considered equal when they are close, as well as when they are exactly the same. If that is the case for your application, I recommend defining an equal function that accepts a precision and verifies that the difference between the two values is not greater than the precision. To learn more, research floating point representations of decimal values.
Note that time package documentation has this to say about using pointers:
Programs using times should typically store and pass them as values, not pointers. That is, time variables and struct fields should be of type time.Time, not *time.Time.
So you should probably change the type of createdAt in your struct.
You can use reflect.DeepEqual.
DeepEqual reports whether x and y are “deeply equal,” defined as follows. Two values of identical type are deeply equal if one of the following cases applies. Values of distinct types are never deeply equal.
The documentation then goes on to describe how arrays, structs, functions, pointers and other types are considered to be deeply equal.

Heterogeneous vs Homogeneous

I'm little bit confused about heterogeneous and homogeneous list and array. In OOP context, if I define base class and it's derived class, why is array of base class homogeneous, if I can store derived classes as well? It's same principle, as void pointer in C (i.e. https://gist.github.com/rawcoder/9720851 ). Every literature says, that homogeneous structure is of same type (semantically), so can you please explain this to me little bit more further?
The simplest way of putting it is that instances of a derived type are instances of a base type, so can be stored in a collection of the base type.
It has more to do with the type of the collection than what's stored in it. If a collection has type array[B], and D < B (type D derives from B), then storing a D in an array[B] doesn't violate the type of the array or operations on the array/elements of the array. However, if the array were defined to hold only descendants of B but not B itself (e.g. array[ D < B ]; array<D extends B> in Java-speak), then it would be heterogenous, because it's declared to hold multiple types, rather than a single type. Note that in some programming languages, "heterogeneous" means holds any type (e.g. array[Any?], array<*>), not just multiple different types.
Outside of such languages, union types muddy the waters, because they allow disparate types to be treated as one. array[A | B | C] could be viewed as heterogeneous, or as the homogeneous array[U], where U = A | B | C.
The point of a type system in programming is to ensure a certain level of correctness by restricting what is allowed, based on type. Homogeneity strengthens the typing system, while unions weaken typing, as they:
can allow an instance of one type to be treated as another, causing a run-time type violation, and
allow for some types to not be handled, which is potentially another error.
Some type systems avoid these by using the more restricted sum type (aka "variant type"), which includes type information along with instances, instead of using union types. This prevents both the above issues with union types, restoring homogeneity. However, type systems that have sum types are usually so strong that the concepts of homogeneity and heretogeneity aren't as useful (basically, every collection is homogeneous, even the heterogeneous ones, which are collections of the maximal type from which all other types descend; consider the array[Any?] example above).
Storing subtypes in a homogeneous collection is a consequence of the LSP, which states that a property of a type should be true of a subtype (i.e. subtypes should be substitutable in contexts in place of a supertype). If the method (e.g. operator[], add) that adds an element to an array takes type B, it should also take subtypes. If code operates on elements of a collection expecting they're of type B, it should operate just as correctly on subtypes.
Note this is distinct from the matter of subtype relationships between collection types (e.g. if D < B, is array[D] < array[B]?), which is a matter of (type) variance.

What is the difference between the equality operator and deepEquals in go?

After reading the spec, I have got:
Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
This implies to me that doing structA == structB would mean that the values of each non-blank field in the struct would have fieldA == fieldB applied to it. So why do we need a concept of a deep equals? Because if the struct has fields which are also structs, the information provided implies to me that those fields will be checked for equality using == also, so surely that would trigger traversal down the object graph anyway?
The thing that you are missing is pointers. When doing a == on pointer, should you check the pointer value (two memory addresses) or the pointed value (two vars) ? And when comparing slices, or maps (both of which can be assimilated to a struct made of pointers) ?
The decision of golang's authors was to do a strict comparison with the == operator, and to provide the reflect.DeepEqual method for those that want to compare the content of their slices.
I personnally make an extensive use of reflect.DeepEquals in tests, as the output value of a function may be a pointer, but waht I really want to compare is the content of the output value.

Enums in computer memory

A quote from Wikipedia's article on enumerated types would be the best opening for this question:
In other words, an enumerated type has values that are different from each other, and that can be compared and assigned, but which are not specified by the programmer as having any particular concrete representation in the computer's memory; compilers and interpreters can represent them arbitrarily.
While I understand the definition and uses of enums, I can't yet grasp the interaction between enums and memory — when an enum type is declared without creating an instance of enum type variable, is the type definition stored in memory as a union or a structure? And what is the meaning behind the aforementioned Wiki excerpt?
The Wikipedia excerpt isn't talking specifically about C's enum types. The C standard has some specific requirements for how enums work.
An enumerated type is compatible with either char or some signed or unsigned integer type. The choice of representation is up to the compiler, which must document its choice (it's implementation-defined), but the type must be able to represent all the values of the enumeration.
The values of the enumeration constants start at 0 by default, and increment by 1 for each successive constant:
enum foo {
zero, // equal to 0
one, // equal to 1
two // equal to 2
};
The constants are always of type int, regardless of what the enum type itself is compatible with. (It would have made more sense for the constants to be of the enumerated type; they're of type int for historical reason.)
You can specify values for some or all of the constants -- which means that the values are not necessarily distinct:
enum bar {
two = 2,
deux = 2,
zwei = 2,
one = 1,
dos // implicitly equal to 2
};
Defining an enumerated type doesn't result in anything being stored in memory at run time. If you define an object of the enumerated type, that object's value will be stored in memory (unless it's optimized away), and will occupy sizeof (enum whatever) bytes. It's the same as for objects of any other type.
An enumeration constant is treated as a constant expression. The expression two is treated almost identically to a constant 2.
Note that C++ has some different rules for enum types. Your question is tagged C, so I won't go into details.
It means that the enum constants are not required to be located in memory. You cannot take the addresses of them.
This allows the compiler to replace all references to enum constants with their actual values. For example, the code:
enum { x = 123; }
int y = x;
may compile as if it were:
int y = 123;
When an enum type is declared without creating an instance of enum type variable, is the type definition stored in memory as a union or a structure?
In C, types are mostly compile-time constructs; once the program has been compiled to machine code, all the type information disappears*. Accessing a struct member is instead "access the memory n bytes past this pointer".
So if the compiler inlines all the enums as shown above, then enums do not exist at all in compiled code.
* Except optionally in the debugging info section, but that's usually only read by debuggers.

Untyped set operations in Isabelle

I have the following code in Isabelle:
typedecl type1
typedecl type2
consts
A::"type1 set"
B::"type2 set"
When I want to use union operation with A and B as bellow:
axiomatization where
c0: "A ∩ B = {}"
Since A and B are sets of different types, I get an error of clash of types which makes sense!
I am wondering if there are any counterparts for set operations that they implicitly consider their operands as pure sets (i.e., ignore their types), therefore something like A ∩' B become possible ( ∩' is ∩ operation counterpart in above sense).
PS:
Another workaround is type casting that I wrote this as a separate question here to better organize my questions.
Thanks
Sets in Isabelle/HOL are always typed, i.e., they contain only elements of one type. If you want to have untyped sets, you have to switch to another logic such as Isabelle/ZF.
Similarly, all values in HOL are annotated with their type, and this is fundamental to the logic. Equality, for example, can only be applied to two values of the same type. Hence, there is no notion of equality between values of different types. Consequently, there is no set operation that ignores the type of values, because such an operation would necessarily have to know how to identify values of different types.

Resources