Hacker News new | ask | show | jobs
by suzuki 1529 days ago
> If you have an interface with a single method, or something with a single varying piece of behavior, please strongly consider accepting a function instead.

Agreed. And if you prefer methods to functions, you can define methods on such a function type! In my LINQ in Go (https://github.com/nukata/linq-in-go), I define a higher order function type:

  type Enumerator[T any] func(yield func(element T))
and several methods on it, for example,

  // Where creates an Enumerator which selects elements by appling
  // predicate to each of them.
  func (loop Enumerator[T]) Where(predicate func(T) bool) Enumerator[T] {
        return func(yield func(T)) {
                loop(func(element T) {
                        if predicate(element) {
                                yield(element)
                        }
                })
        }
  }
Naturally, if you need another type parameter for a method, you must define it as a function. For example,

  // Select creates an Enumerator which applies f to each of elements.
  func Select[T any, R any](f func(T) R, loop Enumerator[T]) Enumerator[R] {
        return func(yield func(R)) {
                loop(func(element T) {
                        value := f(element)
                        yield(value)
                })
        }
  }
Now, with the function which converts a slice to an Enumerator,

  // From creates an Enumerator from a slice.
  func From[T ~[]E, E any](x T) Enumerator[E] {
        return func(yield func(E)) {
                for _, element := range x {
                        yield(element)
                }
        }
  }
you can write the following:

  seq := Select(func(e int) int { return e + 100 },
        From([]int{
                7, 8, 9,
        })).Where(func(e int) bool {
        return e%2 != 0
  })
  seq(func(e int) {
        Println(e)
  })
  // Output:
  // 107
  // 109