samber / mo

🦄 Monads and popular FP abstractions, powered by Go 1.18+ Generics (Option, Result, Either...)
https://pkg.go.dev/github.com/samber/mo
MIT License
2.47k stars 80 forks source link

Add .Unpack() to Result and friends to mirror lo Tuple family OR add .Get() to lo Tuple family to mirror mo Result/Option #40

Open jcbhmr opened 2 months ago

jcbhmr commented 2 months ago

I would like to be able to do this:

type myUnpack[T any, U any] interface {
  Unpack() (T, U)
}

func MyFunc() <-chan myUnpack[int, error] {
  c := make(chan myUnpack[int, error])
  go func(){
    defer close(c)
    time.Sleep(200 * time.Milliseconds)
    // c <- mo.Option
    // c <- lo.Tuple2
    // c <- mo.Result
    // c <- whatever
  }()
  return c
}

so that my users downstream don't have to really care whether they got a Result or a Tuple2 or what; they just do this in their code and it works:

import "github.com/jcbhmr/thelib"

func main() {
  v, err := (<-thelib.MyFunc()).Unpack()
  if err != nil {
    log.Fatal(err)
  }
  log.Printf("The result is %d", v)
}

basically I want to have a nice "this is the way you do it" pattern to pass through 2x tuple of values in a chan. i sometimes use a lo.Tuple[int, bool] and sometimes a mo.Result[int] and sometimes an mo.Option[int] all to represent basically the same thing; I want the user to just .Unpack() the chan result and not worry about any additional .map() or other magic this-library-specific details. just unpack it into idiomatic go multivalues lol. does that make sense? i don't want the user to even need to be aware that I'm using lo or mo; they should just .Unpack() to get the plain go-like multireturn (T, bool) or (T, error) or (T, U) pair.

my suggesting to achieve this utopia of .Unpack() meaning "make into go-like multireturn" is to add a .Unpack() to Result in this package and any other related unpackable types like maybe Option -> (T, bool) or something

NEED TO MENTION that this COULD work with .Get() too! Result and Option in this package already implement .Get() idiomatically; its just that the sister package lo doesnt implement Tuple2.Get() https://pkg.go.dev/github.com/samber/lo#Tuple2

type myGet[T any, U any] interface {
  Get() (T, U)
}

func MyFunc() <-chan myGet[int, error] {
  c := make(chan myGet[int, error])
  go func(){
    defer close(c)
    time.Sleep(200 * time.Milliseconds)
    // c <- mo.Option
    // c <- lo.Tuple2
    // c <- mo.Result
    // c <- whatever
  }()
  return c
}
import "github.com/jcbhmr/thelib"

func main() {
  v, err := (<-thelib.MyFunc()).Get()
  if err != nil {
    log.Fatal(err)
  }
  log.Printf("The result is %d", v)
}

tldr is either .Unpack() or .Get() would be nice to have on all these Option Result Tuple types to convert them back to go-idiomatic multivalues that are not lib-specific Option/Result/Tuple types

samber commented 2 months ago

What about adding a .AsTuple() lo.Tuple2[A, B] method to Option, Result, Either?

In that case, it would be compatible with lo.UnzipX.

IMO, we should not create any dependency between both libraries, this is why I like your suggestion. Maybe lo.UnzipX should accept interface, instead of tuple.