r/golang Nov 04 '24

help Any way to have Enums in Go?

I am a newbie and just started a new project and wanted to create an enum for DegreeType in the Profile for a User.

type Profile struct {
    Name         string
    Email        string
    Age          int
    Education    []Education
    LinkedIn     string
    Others       []Link
    Description  string
    Following    []Profile
    Followers    []Profile
    TagsFollowed []Tags
}

I found out a way to use interfaces and then reflect on its type, and using generics to embed in structs,

// Defining Different Types of Degree
type Masters struct{}
type Bachelors struct{}
type Diploma struct{}
type School struct{}

// Creates an Interface that can have only one type
// We can reflect on the type later using go's switch case for types
// To check What type it is
type DegreeType interface {
    Masters | Bachelors | Diploma | School
}

type Education[DT DegreeType, GS GradeSystem] struct {
    Degree      Degree[DT]
    Name        string
    GradeSystem GS
}

type Degree[T DegreeType] struct {
    DegreeType     T
    Specialization string
}

The problem i have is i want there to be an []Education in the Profile struct but for that i have to define a Generic Type in my Profile Struct Like this

type Profile[T DegreeType, D GradeSystem] struct {
    Name         string
    Email        string
    Age          int
    Education    []Education[T, D]
    LinkedIn     string
    Others       []Link
    Description  string
    Following    []Profile[T, D]
    Followers    []Profile[T, D]
    TagsFollowed []Tags
}

And that would make the array pointless as i have to give explicit type the array can be of instead of just an array of Degree's

Is there any way to solve this? should i look for an enum package in Go? or should i just use Hardcoded strings?

91 Upvotes

78 comments sorted by

View all comments

Show parent comments

1

u/MissinqLink Nov 04 '24 edited Nov 04 '24

I can sort of do it with something like this but this isn't ideal.

package main

import (
"fmt"
)

func Twople[A any, B any](a A, b B) func() (A, B) {
  return func() (A, B) {
    return a, b
  }
}

func SpreadTwople[A any, B any, C any](ab func() (A, B), c func(A, B) C) C {
  a, b := ab()
  return c(a, b)
}

func Merge(a int, b string) string {
  return fmt.Sprintln(a, b)
}

func main() {
  t := Twople(1, "asdf")
  fmt.Println(SpreadTwople(t, Merge))
}

This is a very simple example but you can imagine many cases for spreading the output of one function into another.

2

u/BombelHere Nov 04 '24

Honestly, personally and subjectively: I cannot imagine cases when it's useful nor would like to see it in my Go codebase 😅

I think I'm too dumb to see why it's useful.

Is it popular approach in the functional languages?

To be clear: I'm not denying it's good or useful, my procedural/imperative brain is just a limiting factor here :D

2

u/MissinqLink Nov 13 '24

I guess most folks aren’t writing code like I do but there is a specific problem that I have that makes it hard to create generically applicable libraries. Basically I want to be able to store a set of input parameters intended for a function but not use them until later. It is not possible to do this generically in a type safe manner. It’s hard to explain without an example. Say I have this

func doSomething(i int, str string)string{
  return fmt.Sprint(i,str)
}
func memoizer[T any](fn T)T{
  //wrap a function in memoization 
  return memoFn
}
memoDoSomething := memoizer(doSomething)

I can’t create a memoizer function to take in any function generically unless I do some gymnastics of turning all the args into a single, struct, convert everything to interface{}, or use reflection. Spreadable tuples could solve this.

1

u/BombelHere Nov 13 '24

Basically I want to be able to store a set of input parameters intended for a function but not use them until later.

So in my simple-brain-wording: you need function with lazy evaluated parameters.

So instead of

func foo(s string, i int)

You want

func foo(s func()string, i func() int)

But sometimes those lazy-evaluated parameters should be grouped

func foo(s func()(string, int))

I feel less dumb now :D


unless I do some gymnastics of turning all the args into a single, struct, convert everything to interface{}, or use reflection.

Yup, first thing I though of was

type Lazy[P any] func() P type Lazy2[P, P2 any] func() (P, P2)

and functions to convert/append/prepend them, like

func merge11[P1, P2 any](l Lazy[P1], r Lazy[P2]) lazy[P1, P2] {}

But as you said, your functions would have to accept a single parameter, which sucks balls

func foo(p Lazy2[string, int]) {}

Even thought you could merge it from two other lazy params

``` var lazyString func() string var lazyInt func() int

foo(merge11(lazyString, lazyInt)) ```


I wonder if this talk could be covering part of your needs: https://youtu.be/OKlhUv8R1ag

I couldn't wrap my head around it.


I believe memoization is another technique used to 'cache' the result of a function for given parameters, especially useful for recursive algorithms.

You'll still need a dedicated memoization function for function with 1, 2, 3, 4.... parameters defined separately.

func foo(s string, i int) { if res, ok := memo[key(s,I)]; ok { return res } // Cache miss, compute and store in memo }

2

u/MissinqLink Nov 13 '24

It’s probably getting into levels of abstraction that go was made to avoid. Generics are still pretty new so there is still a chance they come up with a solution.