
Simple apply/filter/reduce package in Go - fosforsvenne
https://github.com/robpike/filter
======
inglor
I don't get why someone would rather write a for loop than use declarative
data syntax. If I want to get the names of all administrators doing
`users.Where(user => user.isAdmin()).Select(user => user.Name)` is so much
nicer than using a for loop - or maybe he's suggesting we start writing FOR
loops instead of SQL for our databases too?

~~~
bunderbunder
In most languages, the for loop ends up being faster. Sometimes noticeably so.
Me, I generally start with declarative syntax, but the profiler frequently
tells me to go back and change it.

Being a systems programmer, wonder if it's easier for him to just use the for
loop as a default. Performance demands are always high in systems programming
(because there'll be a whole stack of additional software standing on top of
your code and depending on it for performance), so one might end up needing to
use for loops often enough that it's easier to just use them all the time for
consistency's sake.

~~~
pcwalton
> In most languages, the for loop ends up being faster. Sometimes noticeably
> so. Me, I generally start with declarative syntax, but the profiler
> frequently tells me to go back and change it.

The optimization techniques to make higher-order functions compile down into
the same code as a for loop are well-known. All you have to do is inline,
constant propagate, and maybe SROA. Every optimizing compiler I know of, even
JavaScript, has no problem doing this.

~~~
Veedrac
FWIW, PyPy has a hard time with this since Python's iterators are really
heavyweight.

------
pmahoney
This isn't "reduce" as I know it.

It requires the user function return the same data type as contained by the
slice. Furthermore, for a slice of size 1, it simply returns that single
element.

    
    
      case 1:
      	return in.Index(0)
    
      ...
    
      if !goodFunc(fn, elemType, elemType, elemType) { ... panic }
    

So I could not, for example, reduce a slice of numbers into a struct of
(min,max,mean).

~~~
ryanthejuggler
The "official" way to do this is map, then reduce. The way reduce is meant to
be used exactly matches this implementation.

Consider that you have a large quantity of numbers that you want to get the
min, max, mean for. If you write something like the following:

    
    
        function minMaxMean(list) {
          return list.reduce(function(lastState, n) {
            var min = lastState.min,
                max = lastState.max,
                sum = lastState.sum,
                count = lastState.count;
            if(n < min) min = n;
            if(n > max) max = n;
            sum += n;
            count += 1;
            return {
              min: min,
              max: max,
              sum: sum,
              count: count
            }
          }, {min: Infinity, max: -Infinity, sum: 0, count: 0});
        }
    

...then you're assuming that the reduce function will run _once_ , over a
_single_ list of numbers, _in order_ from left to right. However, if you
implement it as the following:

    
    
        function minMaxMean(list) {
          return list.map(function(n){
            return {
              min: n,
              max: n,
              sum: n,
              count: 1
            }
          }).reduce(function(a, b) {
            return {
              min: (a.min < b.min ? a.min : b.min),
              max: (a.max > b.max ? a.max : b.max),
              sum: a.sum + b.sum,
              count: a.count + b.count
            }
          });
        }
    

... _then_ you can distribute this out across multiple threads/machines/etc,
update it when new data comes in, reduce in any order.

~~~
espadrine
There are many cases of elegantly using reduce() with a different return type
than the list's item type.

Here is a JS function which computes the combined length of all strings in a
list:

    
    
        stringsLen = (strings) => strings.reduce((acc, item) => acc += item.length, 0);
        
        stringsLen(['hello', 'world'])  //> 10
    

Sure, you can argue that this works, but it misses the point.

    
    
        stringsLen = (strings) => strings.map(s => s.length).reduce((acc, n) => acc += n, 0);
        
         stringsLen(['hello', 'world'])

------
EugeneOZ
With so many "interface{}"s Go looks more like weakly-typed.

"Apply takes a slice of type []T and a function of type func(T) T"

And after that golang.org docs are saying "We don't feel an urgency for them"
about generics"...

~~~
NateDad
He does say "don't do this" and that's exactly one of the reasons why...
because you lose compile-time type safety (it's still type safe at runtime,
though). All the reflection is bound to be slow, as well.

~~~
EugeneOZ
My point is not to say "look, this code is dirty". My point is "look, he use
generics in comments, __in his mind __, so it 's natural thing and obviously
should exist in Go". Only this.

------
kyrra
To see the docs, use godoc.org, copy/paste the full URL for the repo into the
search, and you get this[0]. Nicer way to see the API for the package.

[0] [http://godoc.org/robpike.io/filter](http://godoc.org/robpike.io/filter)

------
falcolas
But you can't write generic functions in Go!

Oh, wait. He just did.

As a caveat to my exasperated sarcasm, I do realize he's using reflection to
identify and type the data at runtime, as opposed to compile time as with C++
templating, but this is kind of generalization is still quite useful when
writing general purpose library code.

Personally, I'd not be inclined to use this either, the number of times I've
actually had to write generic code using reflect in my time writing Go could
be counted with one finger.

I do appreciate that it's there, however, since it is what allows the JSON
library to do its magic.

~~~
hyperpape
This only handles functions of type a -> a -> a
([https://github.com/robpike/filter/blob/master/reduce.go](https://github.com/robpike/filter/blob/master/reduce.go)),
whereas a generic reduce takes functions of type a -> a -> b. So this is
certainly not proof that you can write generics in go. See also pmahoney's
comment in this thread:
[https://news.ycombinator.com/item?id=9315721](https://news.ycombinator.com/item?id=9315721).

~~~
epidemian
Minor nitpick: a generic reduce should take functions of type a -> b -> a,
where a is the type of the reduced values, and b is the type of the elements
in the sequence to be reduced.

~~~
hyperpape
Ah, of course.

