On Sun, Jul 5, 2020 at 12:43 PM Martin Tournoij <[email protected]> wrote:

>
> I played around a bit with the go2go playground today; I was wondering
> how useful it would be to implement enums.
>
> Whether using generics like this is a good idea or not is a different
> discussion, it's just an interesting thing to experiment with and see
> how far I could get.
>
> I thought it might be useful to share my experience; I ran in to several
> errors I couldn't really make sense of. This is probably a failure of
> understanding on my part 😅 But I did spend quite some time in front of
> the draft specification trying to figure this out.
>
> All code was run on the "go2go" playground today.
>
> Note I am not usually subscribed to the golang-nuts mailing list, and
> have not read all of the discussions (using Google groups to browse
> archives is a but of a pain), so apologies if this duplicates any
> previous feedback.
>
> Hope it helps.
>
> Cheerio,
> Martin
>
> ---
>
> First, let's define my types:
>
>         type (
>                 Banana  struct{ banana struct{} }
>                 Coconut struct{ coconut struct{} }
>
>                 Fruit interface {
>                         type Banana, Coconut
>                 }
>         )
>
> And then adds a function which only accepts a "Fruit" enum value:
>
>         func show(type enum Fruit)(fruit enum) {
>                 switch (interface{})(fruit).(type) {
>                 case Banana:
>                         fmt.Println("That's just bananas!")
>                 case Coconut:
>                         fmt.Println("I've got a lovely bunch of coconuts!")
>                 default:
>                         fmt.Println("Yeah nah")
>                 }
>         }
>
>         func main() {
>                 show(Banana{})
>                 show(Coconut{})
>         }
>
> The type switch is somewhat ugly and has limitations, as mentioned in
> the design document, but it works for this case.
>
> Moving on, I wanted to add a function which accepts multiple fruits,
> which gives an error:
>
>         // type Coconut of (Coconut literal) does not match inferred type
> Banana for enum
>         func showAll(type enum Fruit)(fruits ...enum) {
>                 for _, f := range fruits {
>                         show(f)
>                 }
>         }
>
> In the spec it actually mentioned that:
>
> > No variadic type parameters. There is no support for variadic type
> > parameters, which would permit writing a single generic function that
> > takes different numbers of both type parameters and regular
> > parameters.
>
> So looks like that's not supported, fait enough, but the error message
> is a bit confusing.


I'm not sure what is going on here, as you didn't show the calling code.
But this is not an example of a variadic type parameter.  It's fine to use
a type parameter for a variadic ordinary parameter.  But note that a
function written this way does not accept all possible Fruit values.  It
accepts a series of values of one specific Fruit type.





> Second try:
>
>         func showAll(type enum Fruit)(fruits []enum) {
>                 for _, f := range fruits {
>                         show(f)
>                 }
>         }
>
>         func main() {
>                 // Fruit does not satisfy Fruit (interface{type Banana,
> Coconut} not found in Banana, Coconut)
>                 showAll([]Fruit{Banana{}})
>         }
>
> I'm not entirely sure what to make of that error 🤔
>

Type constraints are represented as interface types, but they remain two
different things.  Writing []Fruit{Banana{}} gives you a slice of the
interface type Fruit.  The type constraint Fruit permits a type argument
that is either Banana or Coconut.  Note that the type constraint Fruit does
not permit the type argument Fruit.  But that is what you are trying to use.



> Adding a function which returns a Fruit also proved difficult:
>
>         // cannot use (Banana literal) (value of type Banana) as enum
> value in return statement
>         func getBanana(type enum Fruit)() enum {
>                 return Banana{}
>         }
>
>         // cannot convert (Banana literal) (value of type Banana) to enum
>         func getBanana(type enum Fruit)() enum {
>                 return enum(Banana{})
>         }
>
> I'm not entirely sure why this doesn't work 🤔 It does work when you're
> doing something like:
>
>         // getBanana(Banana{})
>         func getBanana(type enum Fruit)(v enum) enum {
>                 return v
>         }
>
> But this is a fairly useless function :-)
>

You didn't show the calls of the earlier functions, so I'm not sure what
happened.  A function

func getBanana(type enum Fruit)() enum {

requires a type argument.  The type argument cannot be inferred from the
regular arguments.  So the only way to call this function is to write
something like

getBanana(Banana{})().

As you say, that's not too useful.



> I also wasn't able to create a list of all Fruit types:
>
>         // undefined: Fruit
>         var FruitList = []Fruit{Banana{}, Coconut{}}
>
>         func FruitList(type enum Fruit)() enum {
>                 // cannot use (Banana literal) (value of type Banana) as
> enum value in array or slice literal
>                 return []enum{Banana{}, Coconut{}}
>         }
>
>         // function type must have no type parameters
>         var FruitList = func(type enum Fruit)() []enum {
>                 return []enum{}
>         }
>

Here again you seem to be mixing up type constraints and interface types, a
confusion supported by the fact that they are both called Fruit.

Ian

-- 
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].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAOyqgcXHA_tEWeEZamLK%3DFo2MAQsK5P%3DtNzfx%3DzC8Fu5c4UT5w%40mail.gmail.com.

Reply via email to