Hello Ian.
If you want to accomplish the same functionality (as map, filter, reduce -
M/F/R) in another way, fine.
My concern would be does it take more or less
code, and is the result easier or harder to read?
Even a simple loop takes 3 lines and allows more opportunity for errors.
Also,
a M/F/R is often easier to make concurrent-safe.
M/F/R do things that copy and append don't, so I don't follow your comparison.
> Map, filter and reduce do not get that advantage, because they
> already require a function whose type must match the type of
> the slice.
bar = map(foo, func(e T) { return f(e) })
Hello Ian.
> I'm reluctant to add increasingly special purpose builtin functions.
I believe that many would consider map, filter and reduce to be fundamental (not
special purpose) functional programming functions.
> There is no obvious endpoint as we head down that road.
By taking one step at a time, then reevaluating, you can avoid overshooting and
achieve a good balance. Not doing anything because the final solution isn't
apparent only guarantees that you won't get there. :)
Map, filter and reduce would be a good step, in my newbie opinion.
Hello Carlos.
I do not suggest that Go is a functional programming language nor would I hope that it ever would be. This doesn’t mean that a few functional features would be a bad thing.
Couldn’t a “for … range” be considered at least semi-functional? And Go isn’t an OO language either, but… J
bar = map(foo, func(e T) { return f(e) })
P.S.
Ø Newbies should learn the language they're trying to learn.
I’m trying! I believe I read that Go was an evolving language. If so, I don’t see how the argument that “Go doesn’t do it now” is relevant.
On the other hand: Simplicity, clarity, expressiveness, concurrency, and maintainability are all virtues which I believe Go tries to achieve. I realize that language complexity (adding M/F/R would make Go more complex) is to be shunned. So there has to be a balance.
If I’m out of line, I apologize.
From: golan...@googlegroups.com [mailto:golan...@googlegroups.com] On Behalf Of Carlos Castillo
Sent: Wed, February 05, 2014 12:24 am
To: golan...@googlegroups.com
Hello Carlos.
f() has to be implemented regardless of whether you use a loop or a map.
--
The f(e) represents arbitrary code, not an actual function.
The power of the map/filter abstraction starts becoming more apparent when you do things like
Filter(someFunc, Map(funcWith3args, vecA, vecB, vecC))
And you realize you should swap map and filter for your particular app for better performance.
The power of the map/filter abstraction starts becoming more apparent when you do things like
Filter(someFunc, Map(funcWith3args, vecA, vecB, vecC))
And you realize you should swap map and filter for your particular app for better performance.
In the example above, just for the map, you have to check that the three vectors are of same length, allocate a new vector, deal with error conditions, etc. So it is more than just a simple loop over range.
And it would be somewhat easier for a compiler to parallelize map/filter than infer a loop is parallelizable. And how you should slice up array computation for best performance would be machine/memory arch. dependent. I would want to do this in well optimized runtime code rather than have that sort of code littered through my program and which may be suboptimal for another machine.
If you really want to make a case for a feature you have to show several real world problems (not a demonstration, not an example... a real world example that you would see running on some system) and show how the syntax helps and do a proper comparison of using/not using it... And not just "it's two characters shorter" or "it hides the complexity".
The best performance is also OS/algorithm specific, so the compiler can't take care of it.
If you really want to make a case for a feature you have to show several real world problems (not a demonstration, not an example... a real world example that you would see running on some system) and show how the syntax helps and do a proper comparison of using/not using it... And not just "it's two characters shorter" or "it hides the complexity".I agree 100%!The best performance is also OS/algorithm specific, so the compiler can't take care of it.Can be parameterized.
Hello Carlos.
Ø Yes, but if it's defined outside the map operation it's somewhat unfair to not count it towards the complexity of map but ignore that such a function is equally usable in a for loop:
Certainly the function has to be counted in both cases. I don’t believe I said otherwise.
Ø Although the map case is shorter, it is not neither drastically so, nor is it significantly safer.
In this example I would consider the main advantage to be the elimination of the 2 variables (i, x).
John
John Souvestre - New Orleans LA - (504) 454-0899
From: golan...@googlegroups.com [mailto:golan...@googlegroups.com] On Behalf Of Carlos Castillo
Sent: Wed, February 05, 2014 2:54 am
To: golan...@googlegroups.com
Subject: Re: [go-nuts] New to Go - map/filter higher-order functions on collections?
Yes, but if it's defined outside the map operation it's somewhat unfair to not count it towards the complexity of map but ignore that such a function is equally usable in a for loop:
It is something to say for the virtue of not just adding stuff to a language.
http://joearms.github.io/2014/02/01/big-changes-to-erlang.html
Apparently Erlang just got maps!
They had records sure but still...
Going slow can be frustrating but it is not without its hard earned merits.
--
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/RKymTuSCHS0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/RKymTuSCHS0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
Whoa, this thread really exploded while I slept! Let me first state that I am, in fact, trying to learn Go and avoid shoehorning my preconceptions into it. I consider this email exploration to be part of that :)
The primary benefit of map and filter (to me) is not in reducing typing, or eliminating new lines. It's entirely an issue of readability.For example, a common issue that is brought up on this mailing list (and which appears in our codebase) is translating an array of object type to an array of an interface type which that object implements. I understand why this translation is necessary, and it is trivial to write using the for loop pattern:
func (p Product) Inventory() []InventoryResponse {r := make([]integrations.InventoryResponse, len(p.inventory))for index, inv := range p.inventory {r[index] = inv}return r}
However, the for loop pattern is used everywhere in go. In order to check that the effect of this code is in fact to copy our list of Products into a list of InventoryResponses we have to closely read the entire function and check that everything matches properly. Many for loops are simply performing a map operation, and that is fine, but when a map does something more complex it can be a subtle change, and easily missed while reading the code. In other words, this code is bad at expressing the intent of the programmer.
If you use map to express this code, then it is immediately obvious to the reader that you are going to perform a 1-1 translation from the source array to a destination array. All that is left to the reader is to examine the enclosed function to understand what that translation is.func (p Product) Inventory() []InventoryResponse {return map(p.inventory, func(i ProductInventory) InventoryResponse {return i})}I find this to be a major enhancement in readability, which outweighs the cost of having an additional method in the standard.
But it's good at expressing what the program does. Show examples where it does something more complex and it's impossible to write it better. I'm guessing you are trying to write one liners instead of separating things out to functions.
The modern programmer thinks a newline is a thousand times harder to
type than any other character. If instead you take a newline as only
one keystroke, which it is, the fact that your program might not fit
on one line is a bearable burden.
On Wednesday, February 5, 2014 6:51:38 PM UTC+2, Sean Talts wrote:Whoa, this thread really exploded while I slept! Let me first state that I am, in fact, trying to learn Go and avoid shoehorning my preconceptions into it. I consider this email exploration to be part of that :)General tip, learn the language and write at least 10,000 lines before proposing changes to it :).
The primary benefit of map and filter (to me) is not in reducing typing, or eliminating new lines. It's entirely an issue of readability.For example, a common issue that is brought up on this mailing list (and which appears in our codebase) is translating an array of object type to an array of an interface type which that object implements. I understand why this translation is necessary, and it is trivial to write using the for loop pattern:func (p Product) Inventory() []InventoryResponse {r := make([]integrations.InventoryResponse, len(p.inventory))for index, inv := range p.inventory {r[index] = inv}return r}This translation isn't free... by writing it as a for-range loop you make it obvious it's performance overhead. Also, this would be more idiomatic:
func (p Product) Inventory() []Response {rs := make([]Response, 0, len(p.inventory))for _, v := range p.inventory {rs = append(rs, somefn(v))}return rs}However, the for loop pattern is used everywhere in go. In order to check that the effect of this code is in fact to copy our list of Products into a list of InventoryResponses we have to closely read the entire function and check that everything matches properly. Many for loops are simply performing a map operation, and that is fine, but when a map does something more complex it can be a subtle change, and easily missed while reading the code. In other words, this code is bad at expressing the intent of the programmer.But it's good at expressing what the program does. Show examples where it does something more complex and it's impossible to write it better. I'm guessing you are trying to write one liners instead of separating things out to functions.
If you use map to express this code, then it is immediately obvious to the reader that you are going to perform a 1-1 translation from the source array to a destination array. All that is left to the reader is to examine the enclosed function to understand what that translation is.func (p Product) Inventory() []InventoryResponse {return map(p.inventory, func(i ProductInventory) InventoryResponse {return i})}I find this to be a major enhancement in readability, which outweighs the cost of having an additional method in the standard.Show a proper real world example... you are still showing snippets of the code.
On Thu, Feb 6, 2014 at 2:28 PM, Sean Talts <sean....@gmail.com> wrote:I was replying specifically to the comment I quoted: "Applying a
>
> Ian, did you mean to imply that you guys simply haven't found a good way to
> implement map and filter, as the content at the link suggests? Or perhaps
> that you'd rather find a good general purpose solution to generics instead
> (potentially because you don't want to pollute the default namespace, or
> because adding functions at the compiler level is a pain in the ass)?
function to elements of a slice is a basic task that shouldn't bereimplemented over and over again for every type." That is the
argument for generics. I don't think we can take that argument and
make it an argument for map and filter. If we do, we really are on a
slippery slope, one that ends with us adding every function that can
be described as a "basic task" to the language. The argument for
adding map and filter to the language has to be better than that.
Whoa, this thread really exploded while I slept! Let me first state that I am, in fact, trying to learn Go and avoid shoehorning my preconceptions into it. I consider this email exploration to be part of that :)The primary benefit of map and filter (to me) is not in reducing typing, or eliminating new lines. It's entirely an issue of readability.For example, a common issue that is brought up on this mailing list (and which appears in our codebase) is translating an array of object type to an array of an interface type which that object implements.
I understand why this translation is necessary, and it is trivial to write using the for loop pattern:func (p Product) Inventory() []InventoryResponse {r := make([]integrations.InventoryResponse, len(p.inventory))for index, inv := range p.inventory {r[index] = inv}return r}However, the for loop pattern is used everywhere in go. In order to check that the effect of this code is in fact to copy our list of Products into a list of InventoryResponses we have to closely read the entire function and check that everything matches properly. Many for loops are simply performing a map operation, and that is fine, but when a map does something more complex it can be a subtle change, and easily missed while reading the code. In other words, this code is bad at expressing the intent of the programmer.If you use map to express this code, then it is immediately obvious to the reader that you are going to perform a 1-1 translation from the source array to a destination array. All that is left to the reader is to examine the enclosed function to understand what that translation is.func (p Product) Inventory() []InventoryResponse {return map(p.inventory, func(i ProductInventory) InventoryResponse {return i})}I find this to be a major enhancement in readability, which outweighs the cost of having an additional method in the standard.
On Thursday, February 6, 2014 2:48:48 AM UTC-5, egon wrote:
On Wednesday, February 5, 2014 6:51:38 PM UTC+2, Sean Talts wrote:Whoa, this thread really exploded while I slept! Let me first state that I am, in fact, trying to learn Go and avoid shoehorning my preconceptions into it. I consider this email exploration to be part of that :)General tip, learn the language and write at least 10,000 lines before proposing changes to it :).How are you counting the lines? Are these all whole lines that I wrote or do edited lines count as well? If I have really long lines, could those count as two lines or should I split them up to increase my score further?
The primary benefit of map and filter (to me) is not in reducing typing, or eliminating new lines. It's entirely an issue of readability.For example, a common issue that is brought up on this mailing list (and which appears in our codebase) is translating an array of object type to an array of an interface type which that object implements. I understand why this translation is necessary, and it is trivial to write using the for loop pattern:func (p Product) Inventory() []InventoryResponse {r := make([]integrations.InventoryResponse, len(p.inventory))for index, inv := range p.inventory {r[index] = inv}return r}This translation isn't free... by writing it as a for-range loop you make it obvious it's performance overhead. Also, this would be more idiomatic:I would like to believe that most devs know how map works and understand that it applies a function to all elements. It should also be obvious that there is a performance overhead involved with that. I'm not convinced that the lack of the `for` keyword makes engineers forget basic principles.
I would argue that it could be more idiomatic to use maps since that is a stronger signal of intent (apply function to all elements, return array of results) than a generic for/range which may be used for absolutely anything.
Also consider the benefits of the compiler knowing about the intent of a loop and how it could optimize based on that information.
Map can very easily be parallelized and split among all available cores evenly.
This reduces the amount of guess-work that the parser/compiler has to do about what a loop is supposed to do and may speed up both compile and run time.
func (p Product) Inventory() []Response {rs := make([]Response, 0, len(p.inventory))for _, v := range p.inventory {rs = append(rs, somefn(v))}return rs}However, the for loop pattern is used everywhere in go. In order to check that the effect of this code is in fact to copy our list of Products into a list of InventoryResponses we have to closely read the entire function and check that everything matches properly. Many for loops are simply performing a map operation, and that is fine, but when a map does something more complex it can be a subtle change, and easily missed while reading the code. In other words, this code is bad at expressing the intent of the programmer.But it's good at expressing what the program does. Show examples where it does something more complex and it's impossible to write it better. I'm guessing you are trying to write one liners instead of separating things out to functions.Ideally functions should be concise and perform a single task. A more complex function could bring out issues that can be far more dangerous than anything a map implementation can do. Go has the ability to be an expressive language, where programmers can reap the benefits of using a compiled, static-typed language while spending less time on boiler plate and book-keeping. Having to rewrite the same basic functions for every method goes against that ideal and results in a lot of repeated, boilerplate, code; the very thing thing that Go fights against. Applying a function to elements of a slice is a basic task that shouldn't be reimplemented over and over again for every type.If you use map to express this code, then it is immediately obvious to the reader that you are going to perform a 1-1 translation from the source array to a destination array. All that is left to the reader is to examine the enclosed function to understand what that translation is.func (p Product) Inventory() []InventoryResponse {return map(p.inventory, func(i ProductInventory) InventoryResponse {return i})}I find this to be a major enhancement in readability, which outweighs the cost of having an additional method in the standard.Show a proper real world example... you are still showing snippets of the code.This is a real world example. Would 10,000 lines be more likely to convince you?
Ian, did you mean to imply that you guys simply haven't found a good way to implement map and filter, as the content at the link suggests? Or perhaps that you'd rather find a good general purpose solution to generics instead (potentially because you don't want to pollute the default namespace, or because adding functions at the compiler level is a pain in the ass)?I'm noticing as I look around the gc source that adding append to the language was quite complicated, so I have a better feeling for why you guys are reluctant to add more generic functions like this. That said, I still think it would be nice to have a more full-fledged collections library (or equivalent) at our disposal.
I'm going to spend some time this weekend trying to add map and filter to the language, so if it was the first case up above I'd be happy to submit a pull request. Otherwise we can just use my fork in the office, hah.