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.