-
Notifications
You must be signed in to change notification settings - Fork 225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Try out the new Go generics proposal with go-linq #96
Comments
Here is my first try and keeping the go-linq original design: https://go2goplay.golang.org/p/gH50GCy32S7 package main
import (
"fmt"
)
type Query(type T) struct {
Iterate func() func() (T, bool)
}
func From(type T)(source []T) Query(T) {
len := len(source)
return Query(T){
Iterate: func() func() (T, bool) {
index := 0
return func() (item T, ok bool) {
ok = index < len
if ok {
item = source[index]
index++
}
return
}
},
}
}
func (q Query(T)) Where(predicate func(T) bool) Query(T) {
return Query(T){
Iterate: func() func() (T, bool) {
next := q.Iterate()
return func() (item T, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if predicate(item) {
return
}
}
return
}
},
}
}
func (q Query(T)) ToSlice() (r []T) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
r = append(r, item)
}
return
}
func main() {
sliceInt := []int{1, 2, 3, 4, 5}
sliceInt = From(sliceInt).
Where(func(i int) bool {
return i < 4
}).ToSlice()
fmt.Println("Int:", sliceInt)
sliceStr := []string{"a", "b", "c", "d", "e"}
sliceStr = From(sliceStr).
Where(func(i string) bool {
return i < "d"
}).ToSlice()
fmt.Println("Str:", sliceStr)
} |
That’s a good start. I didn’t think a whole lot about this. But in this example basically seems the whole chain works only on the initial type provided. What if we say From(ints) but the result of the chain returns []string? If there was a way to instruct certain methods such as ForEach<int,string>(ints) that could return []string that would be nice. I suspect that doesn’t exist. So that makes me wonder if we need to integrate with generics at all. |
The proposed way will probably be a breaking change in the library. All https://go2goplay.golang.org/p/CFQH-AKIfCU package main
import (
"fmt"
"strconv"
)
type Query(type T) struct {
Iterate func() func() (T, bool)
}
func From(type TSource)(source []TSource) Query(TSource) {
len := len(source)
return Query(TSource){
Iterate: func() func() (TSource, bool) {
index := 0
return func() (item TSource, ok bool) {
ok = index < len
if ok {
item = source[index]
index++
}
return
}
},
}
}
func Select(type TSource, TResult)(q Query(TSource), selector func(TSource) TResult) Query(TResult) {
return Query(TResult){
Iterate: func() func() (TResult, bool) {
next := q.Iterate()
return func() (item TResult, ok bool) {
var it TSource
it, ok = next()
if ok {
item = selector(it)
}
return
}
},
}
}
func (q Query(T)) Where(predicate func(T) bool) Query(T) {
return Query(T){
Iterate: func() func() (T, bool) {
next := q.Iterate()
return func() (item T, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if predicate(item) {
return
}
}
return
}
},
}
}
func (q Query(T)) ToSlice() (r []T) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
r = append(r, item)
}
return
}
func main() {
sliceStr := []int{1, 2, 3, 4, 5}
sliceInt := Select(
From(sliceStr).Where(func(i int) bool { return i < 4 }),
strconv.Itoa,
).ToSlice()
fmt.Println("Select:", sliceInt)
} |
Here is another try, more complex and playing around with the |
That's excellent. So we can do that after all I guess. Which part of the code you're referring to by "constructor"? |
What I meant was, this kind of implementation would not be possible: func From(source interface{}) Query {
src := reflect.ValueOf(source)
switch src.Kind() {
case reflect.Slice, reflect.Array:
return FromSlice(...)
case reflect.Map:
return FromMap(...)
case reflect.String:
return FromString(...)
case reflect.Chan:
return FromChannel(...)
default:
return FromIterable(...)
}
} We would need to use the specific "Query constructor" function in each particular type. |
If getting rid of type-agnostic |
Generics proposal is now accepted. |
So I tried to imitate what
However this is not supported by design. https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#methods-may-not-take-additional-type-arguments |
@ahmetb are there any plans to still try to implement this lib with generics since go 1.18 beta has been released? |
Based on my last experimentation, Go generics unfortunately isn't advanced enough to support this by design. But I also encourage others to try Select().Order() where each method takes a generic type and returns another generic type processed further down the chain. |
Hi, i made myself an early and naive one here https://github.com/szmcdull/glinq. Would you have a try? |
@ahmetb Have you considered using Facilitators to mitigate the limitation of having no generic parameterized methods? See https://rakyll.org/generics-facilititators/ |
@StevenACoffman sadly the main problem with Go generics is the chaining won't work the way we currently have in this library. If you look at @szmcdull's I am yet to see a feasible way to achieve the chaining approach we currently have with Go generics. In the meanwhile, other libraries like |
Go now has a prototype of generics implementation. Here are some resources:
This potentially could give a severe performance boost to go-linq, as well as actually make this library useful.
I haven't taken a closer look at how we would do this yet. For example, we might still end up having to do some type assertions. However, it might help us redesign the package and release a v4 if the generics proposal is adopted.
The text was updated successfully, but these errors were encountered: