#Generic Type Parameter on Receiver Function

23 messages · Page 1 of 1 (latest)

main shale
#

I’m incredibly new at go and have been fiddling with generics. I’ve declared a function type that should take one type and convert it to another type. This works as expected.

type MapperFunction[TInput any, TOutput any] func(input TInput) TOutput

I have a struct Offshoot[T] and want to now create a receiver function that takes in this mapper func type. I cannot figure out how to pass the [TOutput] type to receiver function so the compiler knows what type to return from the mapper func. The below code is invalid and simply demonstrates the goal.

func (offshoot *Offshoot[TInput]) Mapper(mapper MapperFunction[TInput, TOutput]) Offshoot[TOutput] {
  // call mapper and return new Offshoot[TOutput]
}

I’m able to declare the function as below, however this doesn’t work against an instance of Offshoot and requires passing it as a parameter.


func MapperT[TInput any, TOutput any](offshoot *Offshoot[TInput], mapper MapperFunction[TInput, TOutput]) Offshoot[TOutput] {

dull totem
#

I believe you can not. If you need multiple types in a function with a struct receiver, you need to declare these types for the struct as well.
So in your case Offshoot has to have [TInput, TOutput] in its declaration.
I understand what you're trying to do, but go's generics behave differently, compared to generics in many other languages (unfortunately)

main shale
#

That’s sort of the conclusion I ended up at. I might be able to utilize interfaces to accomplish something similar.

dull totem
#

yeah you can utilize type switch if need to implement it that way

main shale
#

My initial thought won’t work as a receiver function can’t take an interface.

dull totem
#

what are you trying to do? You described it like you're trying to func (i interface{}) fn() tbh

main shale
#

yeah, i was hoping to be able to treat the Offshoot as a 'mappable' interface so I could in effect create a more fluent API.

#
type mappable[TInput any, TOutput any] interface {
    Mapper(mapper MapperFunction[TInput, TOutput])
}

func (m mappable[TInput, TOutput]) Map(){

}
#

effectively learning another new thing about Go 🙂

static karma
#

why not directly call mapper?

var mapper MapperFunction[TInput, TOutput]
mapper(mappableThing)
main shale
#

stressing the fact that this was an experiment with generics and go the goal was not to pass an instance but rather process an instance.

#
off := offshoot.
        Ensure(func(input int) bool {
            return input == 10
        }).
        Ensure(func(input int) bool {
            //t.Errorf("The second ensure function was execued dispite being a failure")
            return true
        })

    result := MapperT[int, string](off, func(input int) string {
        return strconv.Itoa(input)
    })
#

but rather

dull totem
#

tbh I'd suggested plain interfaces. As I already said go generics aren't as powerful as in another languages.
I still don't fully understand what you're trying to do, but something like this should probably do the job (if you want to attach custom logic to a type)

type T int

type Output interface{
    AsFloat() float64
}

func (t *T) AsFloat() float64(){
    return float64(t)
}
#

I wrote it an hour ago and forgot to send it lol

main shale
#
off := offshoot.
Ensure(func(input int) bool {
    return input == 10
}).
Ensure(func(input int) bool {
   
    return true
}).
Mapper(func(input int) string {
    return strconv.Itoa(input)
})
#

and the result would now become a Offshoot[string]

static karma
#

But go is not designed to do processing change, go is not designed as FP

main shale
#

yeah, its totally fine if Go can't support it. Learning new things and understanding where the limits exist is an important part when learning a new language.

static karma
#

You can try define:

type Mapper[InT any, OutT any] func(in InT)(out OutT)

func MapSlice[InT any, OutT any](slice []InT, m Mapper[InT, OutT])(res []OutT){
  res = make([]OutT, len(slice))
  for i, v := range slice {
    res[i] = m(v)
  }
  return
}

off := MapSlice(offshoot.
  Ensure(func(input int) bool {
    return input == 10
  }).
  Ensure(func(input int) bool {
   
    return true
  }).
  AsSlice(), func(input int) string {
    return strconv.Itoa(input)
})

#

so use a independent function MapSlice instead put everything on a type

#

The chain call is most used with OOP I think, so it can reuse codes, but go is not OOP