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(&ages))

// 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(&ages) 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(&ages))

// 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(&ages) 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?

-- 
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/e180a4cd-0d5e-4b91-a238-b668206095acn%40googlegroups.com.

Reply via email to