lo icon indicating copy to clipboard operation
lo copied to clipboard

Add Support for pipeline usage

Open yxlimo opened this issue 2 years ago • 9 comments

like this

type Foo struct {
    ID string
}
value := []*Foo{{ID: "a"}, {ID: "b"}, {ID: "c"}}
pipe := lo.NewPipeline(value)
result := pipe.Map(func(v *Foo) string { return v.ID }).Filter(func(v *Foo) bool { return v.ID == "a" }).Uniq().Result()

yxlimo avatar Apr 25 '22 06:04 yxlimo

Hi @yxlimo and thanks for the proposal.

We already have some "mixed" helpers, such as MapFilter. But I agree with you, we should have something more ambitious.

Unfortunately, Go does not offer method-level generics, for now. So you would need to declare every type from lo.NewPipeline constructor.

I would suggest something like:

import "github.com/samber/lo/x

x.Pipe(
    x.Map[Foo, string](...),
    x.Filter[string](...),
    x.Map[string, int](...),
    x.Uniq[int](...),
)

With Pipe doing some reflection for transmitting arguments over the functions. it can not be considered as safe, but it could be a good start until Go dev team add support for method-level generics.

WDYT?

Did you imagine a better workaround? 🤔

samber avatar Apr 25 '22 11:04 samber

Maybe type-safe function (not method)

func Map[T, U any] (in *Pipe[T], cb func(T) U) (out *Pipe[U])

where Pipe is proposed object with bunch of methods can be good workaround.

xakep666 avatar May 04 '22 14:05 xakep666

Maybe type-safe function (not method)

func Map[T, U any] (in *Pipe[T], cb func(T) U) (out *Pipe[U])

where Pipe is proposed object with bunch of methods can be good workaround.

Good idea but still not that easy to use, and I suppose it to be something like %>% operator in R language. Maybe here we should use . operator but this can't come together with existing . operator. Your Pipe is much like Stream in java, I guess? Actually this package is my first time experience of functional programming in Go language, it's great.

orbitoo avatar May 05 '22 06:05 orbitoo

@xakep666 the prototype you propose is the same than the current lo.Map, lo.Filter... functions.

What should Pipe[T] do in your example?

samber avatar May 05 '22 08:05 samber

@xakep666 the prototype you propose is the same than the current lo.Map, lo.Filter... functions.

What should Pipe[T] do in your example?

My thoughts for example

type Pipe[T any] []T

func NewPipe[T any](source []T) *Pipe[T] {
	pipe := Pipe[T](source)
	return &pipe
}

func (p *Pipe[T]) Map[U any](f func(T,int) U) *Pipe[U] {
	v := lo.Map([]T(*p), f)
	return NewPipe(v)
}

// ... (other functions as pipe methods)

func (p *Pipe[T]) Get() []T {
	return []T(*p)
}

orbitoo avatar May 05 '22 08:05 orbitoo

@xakep666 the prototype you propose is the same than the current lo.Map, lo.Filter... functions.

What should Pipe[T] do in your example?

just wrap slices with pipes, and change functions into pipe methods, whose return type is pipe as well.

add methods to create pipe with exsisting slices and get results from a pipe.

orbitoo avatar May 05 '22 08:05 orbitoo

@xakep666 the prototype you propose is the same than the current lo.Map, lo.Filter... functions.

What should Pipe[T] do in your example?

Oh, I forget that a method can't use generic...

orbitoo avatar May 05 '22 09:05 orbitoo

Pipe can be used to incapsulate lazy evaluations for example. Methods are similar to lo package functions.

xakep666 avatar May 05 '22 09:05 xakep666

I'd link to streams design for this purpose http://nessos.github.io/Streams

alrz avatar Aug 27 '22 09:08 alrz