Re: [go-nuts] Generics: Whats the best way around limitation that methods can't be parameterized

2022-01-20 Thread 'Dan Kortschak' via golang-nuts
On Thu, 2022-01-20 at 19:05 -0800, Mandolyte wrote:
> Or perhaps a hybrid, where the methods call generic functions...
>
> On Thursday, January 20, 2022 at 7:23:37 PM UTC-5 Ian Lance Taylor
> wrote:
> > On Thu, Jan 20, 2022 at 3:30 AM Travis Keep 
> > wrote:
> > >
> > > I am working on a Pipeline[T, U any] type which receives any
> > number of T values and in turn emits U values. Pipelines are used
> > to read from databases.
> > >
> > > For instance, this code fetches the ages of 5 people under 50.
> > >
> > > int[] ages
> > > PeopleOrderedByName(Start[Person]().
> > > Map(func(p Person) int { return p.Age }).
> > > Filter(func(age int) bool { return age < 50 }).
> > > Slice(0, 5).
> > > AppendTo())
> > >
> > > // Output: {49, 19, 25, 34, 42}
> > > fmt.Println(ages)
> > >
> > > Some notes about this code:
> > >
> > > 1. Start[Person]() returns a Pipeline[Person, Person] that emits
> > each Person value it receives.
> > > 2. the Map method on Pipeline is parameterized. What Map returns
> > depends on the return value of the mapper function passed in. In
> > the example above, Map() returns a Pipeline[Person, int]
> > > 3. AppendTo() returns a Consumer[Person] which takes Person
> > values without emitting anything. As a side effect, this
> > Consumer[Person] appends the ages of people under 50 to the ages
> > slice.
> > > 4. Even if there are millions of people in the database, this
> > code will read just enough people to get 5 ages under 50.
> > > 5. This code won't work in 1.18 because methods in GO on a
> > parameterized type like Pipeline cannot be further parameterized,
> > but in this API, the Map() method has to be parameterized.
> > >
> > > To get around this limitation, I revised my API to look like
> > this:
> > >
> > > int[] ages
> > > PeopleOrderedByName(
> > > SendTo[Person, int](
> > > Start().
> > > Map(func(person interface{}) interface{} { return
> > person.(Person).Age }).
> > > Filter(func(age interface{}) bool { return age.(int) < 50 }).
> > > Slice(0, 5),
> > > AppendTo())
> > >
> > > // Output: {49, 19, 25, 34, 42}
> > > fmt.Println(ages)
> > >
> > > Notes about this API:
> > >
> > > 1. Pipeline is no longer a parameterized type. Pipeline instances
> > receive interface{} values and emit interface{} values.
> > > 2. AppendTo() returns a Consumer[int] that appends all the
> > values it receives to the ages slice.
> > > 3. SendTo[Person, int]() returns a Consumer[Person] that
> > applies a pipeline to the Person values it receives and then sends
> > the emitted values to the Consumer[int] passed as a second value.
> > > 4. This API suffers all the disadvantages that generics aims to
> > solve. The pipeline code is clunkier because values have to be
> > constantly converted from interface{}, Problems with type
> > mismatches become runtime errors instead of compile time errors,
> > storing a Person value in an interface results in an extra memory
> > allocation etc.
> > >
> > > Is there a more elegant way around the limitation that methods on
> > parameteried types can't be parameterized?
> >
> >
> > I don't know about "more elegant" but I would suggest not trying
> > to
> > use a fluent API. Use a function based API instead. Maybe it would
> > look like
> >
> > p := pipelinePeopleOrderedByName(Start[Person]()
> > p = pipeline.Map(p, func(p Person) int { return p.Age })
> > p = pipeline.Filter(p, func(age int) bool { return age < 50 })
> > p = pipeline.Slice(p, 0, 5)
> > ages = append(ages, pipeline.Results(p))
> >
> > (Of course I've omitted the error handling, but your example did as
> > well.)
> >
> > Ian


I saw a post at JBD's blog about making this easier:
https://rakyll.org/generics-facilititators/


-- 
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 golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/7c8eba6ce27db310daec8c3a34d53cb8c233ddd6.camel%40kortschak.io.


Re: [go-nuts] Generics: Whats the best way around limitation that methods can't be parameterized

2022-01-20 Thread Mandolyte
Or perhaps a hybrid, where the methods call generic functions...

On Thursday, January 20, 2022 at 7:23:37 PM UTC-5 Ian Lance Taylor wrote:

> On Thu, Jan 20, 2022 at 3:30 AM Travis Keep  wrote:
> >
> > I am working on a Pipeline[T, U any] type which receives any number of T 
> values and in turn emits U values. Pipelines are used to read from 
> databases.
> >
> > For instance, this code fetches the ages of 5 people under 50.
> >
> > int[] ages
> > PeopleOrderedByName(Start[Person]().
> > Map(func(p Person) int { return p.Age }).
> > Filter(func(age int) bool { return age < 50 }).
> > Slice(0, 5).
> > AppendTo())
> >
> > // Output: {49, 19, 25, 34, 42}
> > fmt.Println(ages)
> >
> > Some notes about this code:
> >
> > 1. Start[Person]() returns a Pipeline[Person, Person] that emits each 
> Person value it receives.
> > 2. the Map method on Pipeline is parameterized. What Map returns depends 
> on the return value of the mapper function passed in. In the example above, 
> Map() returns a Pipeline[Person, int]
> > 3. AppendTo() returns a Consumer[Person] which takes Person values 
> without emitting anything. As a side effect, this Consumer[Person] appends 
> the ages of people under 50 to the ages slice.
> > 4. Even if there are millions of people in the database, this code will 
> read just enough people to get 5 ages under 50.
> > 5. This code won't work in 1.18 because methods in GO on a parameterized 
> type like Pipeline cannot be further parameterized, but in this API, the 
> Map() method has to be parameterized.
> >
> > To get around this limitation, I revised my API to look like this:
> >
> > int[] ages
> > PeopleOrderedByName(
> > SendTo[Person, int](
> > Start().
> > Map(func(person interface{}) interface{} { return person.(Person).Age }).
> > Filter(func(age interface{}) bool { return age.(int) < 50 }).
> > Slice(0, 5),
> > AppendTo())
> >
> > // Output: {49, 19, 25, 34, 42}
> > fmt.Println(ages)
> >
> > Notes about this API:
> >
> > 1. Pipeline is no longer a parameterized type. Pipeline instances 
> receive interface{} values and emit interface{} values.
> > 2. AppendTo() returns a Consumer[int] that appends all the values 
> it receives to the ages slice.
> > 3. SendTo[Person, int]() returns a Consumer[Person] that applies a 
> pipeline to the Person values it receives and then sends the emitted values 
> to the Consumer[int] passed as a second value.
> > 4. This API suffers all the disadvantages that generics aims to solve. 
> The pipeline code is clunkier because values have to be constantly 
> converted from interface{}, Problems with type mismatches become runtime 
> errors instead of compile time errors, storing a Person value in an 
> interface results in an extra memory allocation etc.
> >
> > Is there a more elegant way around the limitation that methods on 
> parameteried types can't be parameterized?
>
>
> I don't know about "more elegant" but I would suggest not trying to
> use a fluent API. Use a function based API instead. Maybe it would
> look like
>
> p := pipelinePeopleOrderedByName(Start[Person]()
> p = pipeline.Map(p, func(p Person) int { return p.Age })
> p = pipeline.Filter(p, func(age int) bool { return age < 50 })
> p = pipeline.Slice(p, 0, 5)
> ages = append(ages, pipeline.Results(p))
>
> (Of course I've omitted the error handling, but your example did as well.)
>
> 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 golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/163d0bd5-a50f-45d0-96bb-1129685a5037n%40googlegroups.com.


Re: [go-nuts] Generics: Whats the best way around limitation that methods can't be parameterized

2022-01-20 Thread Ian Lance Taylor
On Thu, Jan 20, 2022 at 3:30 AM Travis Keep  wrote:
>
> I am working on a Pipeline[T, U any] type which receives any number of T 
> values and in turn emits U values.  Pipelines are used to read from databases.
>
> For instance, this code fetches the ages of 5 people under 50.
>
> int[] ages
> PeopleOrderedByName(Start[Person]().
> Map(func(p Person) int { return p.Age }).
> Filter(func(age int) bool { return age < 50 }).
> Slice(0, 5).
> AppendTo())
>
> // Output: {49, 19, 25, 34, 42}
> fmt.Println(ages)
>
> Some notes about this code:
>
> 1. Start[Person]() returns a Pipeline[Person, Person] that emits each Person 
> value it receives.
> 2. the Map method on Pipeline is parameterized. What Map returns depends on 
> the return value of the mapper function passed in. In the example above, 
> Map() returns a Pipeline[Person, int]
> 3. AppendTo() returns a Consumer[Person] which takes Person values 
> without emitting anything.  As a side effect, this Consumer[Person] appends 
> the ages of people under 50 to the ages slice.
> 4. Even if there are millions of people in the database, this code will read 
> just enough people to get 5 ages under 50.
> 5. This code won't work in 1.18 because methods in GO on a parameterized type 
> like Pipeline cannot be further parameterized, but in this API, the Map() 
> method has to be parameterized.
>
> To get around this limitation, I revised my API to look like this:
>
> int[] ages
> PeopleOrderedByName(
> SendTo[Person, int](
> Start().
> Map(func(person interface{}) interface{} { return person.(Person).Age 
> }).
> Filter(func(age interface{}) bool { return age.(int) < 50 }).
> Slice(0, 5),
> AppendTo())
>
> // Output: {49, 19, 25, 34, 42}
> fmt.Println(ages)
>
> Notes about this API:
>
> 1. Pipeline is no longer a parameterized type. Pipeline instances receive 
> interface{} values and emit interface{} values.
> 2. AppendTo() returns a Consumer[int] that appends all the values it 
> receives to the ages slice.
> 3. SendTo[Person, int]() returns a Consumer[Person] that applies a 
> pipeline to the Person values it receives and then sends the emitted values 
> to the Consumer[int] passed as a second value.
> 4. This API suffers all the disadvantages that generics aims to solve.  The 
> pipeline code is clunkier because values have to be constantly converted from 
> interface{}, Problems with type mismatches become runtime errors instead of 
> compile time errors, storing a Person value in an interface results in an 
> extra memory allocation etc.
>
> Is there a more elegant way around the limitation that methods on 
> parameteried types can't be parameterized?


I don't know about "more elegant" but I would suggest not trying to
use a fluent API.  Use a function based API instead.  Maybe it would
look like

p := pipelinePeopleOrderedByName(Start[Person]()
p = pipeline.Map(p, func(p Person) int { return p.Age })
p = pipeline.Filter(p, func(age int) bool { return age < 50 })
p = pipeline.Slice(p, 0, 5)
ages = append(ages, pipeline.Results(p))

(Of course I've omitted the error handling, but your example did as well.)

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 golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAOyqgcVzTJXXkOL0sGRjTDv9vt6%2B-cf9s7RzCDnuFe9JFCsRyg%40mail.gmail.com.