Great to see you playing with generics.

I'll leave it up to the experts to reply about whether the compilation 
problems are bugs.

But I have a suggestion:  don't try to define Vec in an interface with a 
type set.  Just use T[] -- looks like it might simplify things.

Scott
On Tuesday, August 24, 2021 at 12:55:55 AM UTC+2 Changkun Ou wrote:

> Hi golang-nuts,
>
> I am trying out the latest type parameter and type sets design.
> The purpose is to implement a Clamp function that works for numbers and 
> vectors.
>
> The version for numbers is straightforward and easy:
>
> ```go
> // Number is a type set of numbers.
> type Number interface {
> ~int | ~int8 | ~int32 | ~int64 | ~float32 | ~float64
> }
>
> // Clamp clamps a given value in [min, max].
> func Clamp[N Number](n, min, max N) N {
> if n < min { return min }
> if n > max { return max }
> return n
> }
> ```
>
> Everything is good so far. Then, let's define vector types:
>
> ```go
> // Vec2 represents a 2D vector (x, y).
> type Vec2[N Number] struct {
> X, Y N
> }
>
> // Vec3 represents a 3D vector (x, y, z).
> type Vec3[N Number] struct {
> X, Y, Z N
> }
>
> // Vec4 represents homogeneous coordinates (x, y, z, w) that defines
> // either a point (W=1) or a vector (W=0). Other case of W need to apply
> // a perspective division to get the actual coordinates of X, Y, Z.
> type Vec4[N Number] struct {
> X, Y, Z, W N
> }
> ```
>
> However, in order to declare a type set of all possible vectors, I tried
> two possibilities:
>
> ```go
> // Vec is a type set of vectors.
> type Vec[N Number] interface {
> Vec2[N] | Vec3[N] | Vec4[N] // ERROR: interface cannot have type 
> parameters
> }
> ```
>
> ```go
> type Vec interface {
> Vec2[N Number] | Vec3[N Number] | Vec4[N Number] // ERROR: interface 
> cannot have type parameters
> }
> ```
>
> Let's just enumerates all possibilities for the Vec type set:
>
> ```go
> // Vec is a type set of vectors.
> type Vec interface {
> Vec2[float32] | Vec3[float32] | Vec4[float32] |
> Vec2[float64] | Vec3[float64] | Vec4[float64]
> }
> ```
>
> However, with this definition, it remains very tricky to construct a
> generic implementation for a clamp function:
>
> ```go
> // ERROR: this function does not compile
> func ClampVec[V Vec, N Number](v V, min, max N) V {
> switch (interface{})(v).(type) {
> case Vec2[float32]:
> return Vec2[float32]{
> Clamp[float32](v.X, min, max),
> Clamp[float32](v.Y, min, max),
> }
> case Vec2[float64]:
> return Vec2[float64]{
> Clamp[float64](v.X, min, max),
> Clamp[float64](v.Y, min, max),
> }
> case Vec3[float32]:
> return Vec3[float32]{
> Clamp[float32](v.X, min, max),
> Clamp[float32](v.Y, min, max),
> Clamp[float32](v.Z, min, max),
> }
> case Vec3[float64]:
> return Vec3[float64]{
> Clamp[float64](v.X, min, max),
> Clamp[float64](v.Y, min, max),
> Clamp[float64](v.Z, min, max),
> }
> case Vec4[float32]:
> return Vec4[float32]{
> Clamp[float32](v.X, min, max),
> Clamp[float32](v.Y, min, max),
> Clamp[float32](v.Z, min, max),
> Clamp[float32](v.W, min, max),
> }
> case Vec4[float64]:
> return Clamp[float64]{
> Clamp[float64](v.X, min, max),
> Clamp[float64](v.Y, min, max),
> Clamp[float64](v.Z, min, max),
> Clamp[float64](v.W, min, max),
> }
> default:
> panic(fmt.Sprintf("unexpected type %T", v))
> }
> }
> ```
>
> I wish I could converge to a version similar like this:
>
> ```go
> func Clamp[N Number](n, min, max N) N {
> if n < min { return min }
> if n > max { return max }
> return n
> }
>
> // ERROR: this functions does not compile
> func ClampVec[N Number, V Vec[N]](v V[N], min, max N) V[N] {
> switch (interface{})(v).(type) {
> case Vec2[N]: // If V is Vec2[N], then return a Vec2[N].
> return Vec2[N]{
> Clamp[N](v.X, min, max),
> Clamp[N](v.Y, min, max),
> }
> case Vec3[N]: // Similar
> return Vec3[N]{
> Clamp[N](v.X, min, max),
> Clamp[N](v.Y, min, max),
> Clamp[N](v.Z, min, max),
> }
> case Vec4[N]: // Similar
> return Vec4[N]{
> Clamp[N](v.X, min, max),
> Clamp[N](v.Y, min, max),
> Clamp[N](v.Z, min, max),
> Clamp[N](v.W, min, max),
> }
> default:
> panic(fmt.Sprintf("unexpected type %T", v))
> }
> }
>
> // caller side:
>
> Clamp[float32](256, 0, 255) // 255
> Clamp[float64, Vec2[float64]]({1, 2, 3}, 0, 1) // Vec2[float32]{1, 1, 1}
> ...
> ```
>
> I found myself trapped and not able to further proceed. Is the above code 
> legal
> with the current design but just because the compiler has not implemented 
> it yet?
> Any ideas on how could the current design be able to produce something 
> even simpler?
>
> Thank you in advance for your read and help.
>

-- 
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 golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/d4aa1117-f6f1-4260-b6c2-72211ac771c0n%40googlegroups.com.

Reply via email to