Live data from Hacker News

Simple apply/filter/reduce package in Go

github.com

41–50 of 68 posts

Re: Simple apply/filter/reduce package in Go

#41

Earlier quoted context omitted.

It's 30 lines long (the equivalent in Haskell would be 1 fairly simple line of code). Type errors are detected at runtime instead of at compile type, and it's not as generic as actual reduce.

> It's 30 lines long (the equivalent in Haskell would be 1 fairly simple line of code). Bully for Haskell. Language N can always do it better/faster/shorter than language M. What matters in this case is that it can be done, in a type safe way. > Type errors are detected at runtime instead of at compile type Already mentioned that. > it's not as generic as actual reduce. It's toy code, forgive him for not writing it p…

> If it can accept one ambiguous type throughout, there's no reason it could not accept multiple types throughout.

I'm sure it could, but only at the expense of ballooning to an even more disproportionate length.

Re: Simple apply/filter/reduce package in Go

#42

Earlier quoted context omitted.

GvR discourages them if that's a strong enough reason for you :)

its not, actually. he seems to discourage them on the grounds that a list comprehension would be better. however, that's just alternate syntax for a logically equivalent operation. personally, I use whichever one is more clear to me based on context. I appreciate having the option. Maybe you say that's un-Pythonic because "there should be just one way to do things", but I'm not particularly dogmatic and not persuaded…

List comprehension produces a different output than reduce (since reduce does not have to produce a list). Reduce and for loops are better compared.

For loops make the logic occurring on each operator explicit and in the current frame of reference, as opposed to reduce, which splits the logic from the point of operation.

Also, when reduce is used to produce an artifact similar to a list comprehension: the list comprehension is going to be better optimized, more comprehensible for many programmers, and with two character changes can be turned into a generator instead of a memory-heavy list.

Re: Simple apply/filter/reduce package in Go

#43

Earlier quoted context omitted.

> It's 30 lines long (the equivalent in Haskell would be 1 fairly simple line of code). Bully for Haskell. Language N can always do it better/faster/shorter than language M. What matters in this case is that it can be done, in a type safe way. > Type errors are detected at runtime instead of at compile type Already mentioned that. > it's not as generic as actual reduce. It's toy code, forgive him for not writing it p…

> If it can accept one ambiguous type throughout, there's no reason it could not accept multiple types throughout. I'm sure it could, but only at the expense of ballooning to an even more disproportionate length.

> I'm sure it could, but only at the expense of ballooning to an even more disproportionate length.

If you looked at the code, you would see that it could be done in zero additional lines of code if he wanted, or in one if he wanted to be explicit.

    if !goodFunc(fn, elemType, zero.Type().Elem(), elemType) {
        str := elemType.String()
        panic("apply: function must be of type func(" + str + ", " + zero.Type().Elem().String() + ") " + str)
    }
There would be a few other inline changes to add `zero` to the function calls (and fix the 0/1 cases), but they are all pretty trivial.

If we want to be pedantic about it, the goodFunc call is not even technically required, it just makes the error output a bit better.

Re: Simple apply/filter/reduce package in Go

#44

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"...

Looks are deceiving. `interface{}` != `void *`. The value inside the `interface{}` box is still strongly typed, it's just boxed to make function interfaces simpler (given Go's lack of generics).

I don't want to say interface{}=void (at least while type assertion isn't used). But please read comment to Apply function [0] and tell me you don't see usual generics record.

[0] https://github.com/robpike/filter/blob/master/apply.go#L19

Re: Simple apply/filter/reduce package in Go

#45

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"...

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.

Re: Simple apply/filter/reduce package in Go

#46
post #4

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?

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 o…

I'm not sure what languages you are referring to, but Rust and D's iterator methods compile to the same code that you would write if you were using raw loops.

Re: Simple apply/filter/reduce package in Go

#47
post #45

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"...

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.

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.

Re: Simple apply/filter/reduce package in Go

#48
post #4

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?

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 o…

> 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.

Re: Simple apply/filter/reduce package in Go

#49
post #22

Earlier quoted context omitted.

That's not why clojure has transducers. You can use a mapping transducer to express map, but it's actually going to have slightly worse performance than regular map. There's nothing inherently slow about higher order collection functions, rust has them and they compile to the same machine code as the equivalent loop construct. Its just that most languages implement them on the wrong data structures. Functional langua…

.NET implements them on iterators, too, but that's not an inherently fast approach. .NET iterators are objects that implement a specific interface, and iterating over them involves two function calls per element - one to move to the next item, and one to retrieve the current item. Those two function calls introduce an overhead that may be significant in a "tight loop", and that may not exist in a hand-coded for loop.…

> .NET iterators are objects that implement a specific interface, and iterating over them involves two function calls per element - one to move to the next item, and one to retrieve the current item.

Any optimizing compiler will inline those functions. "Avoid function calls for performance" hasn't been relevant optimization advice for at least a decade.

Re: Simple apply/filter/reduce package in Go

#50
post #4

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?

    var admins = users.Where(user => user.isAdmin()).Select(user => user.Name)
vs

    var admins []string
    for _, user := range users {
        if user.IsAdmin() {
            admins = append(admins, user.Name)
        }
    }
So, at the end of the day, these two pieces of code do the exact same thing, and have mostly the exact same meaning. I read them almost identically...

Make a list of admins. iterate through the users, if the user is an admin, append its name to the list of admins.

The unrolled version is a lot easier to modify later. What if you decide that each of these admin users should have a star next to their name? For the go code, you just add one line:

    var admins []string
    for _, user := range users {
        if user.IsAdmin() {
            admins = append(admins, user.Name)
            user.Name = user.Name + "*"
        }
    }
for the C# code, you either have to write a second LINQ query (and now you're iterating the list twice unnecessarily), or you have to unroll it into a normal loop.

So now you've added complexity to the language that is only really good for saving carriage returns, and it makes your code harder to maintain. Sure, it's pretty and interesting from a programming theory POV... but at the end of the day, I don't get paid to write pretty or interesting code. I do get paid to write maintainable code.

Post reply on HN