Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> But it does highlight the key failing of for loops: they conflate three separate kinds of operations -- filtering, reduction and transformation.

Unsurprising for a Haskeller, but he forgot the fourth and likely most common use of for loops: iterative code that has side effects.



Or in fact anything that is sequential. There are a lot of loops that don't necessarily have side effects but are still much easier to express as sequential calculation. If f(a[i]) depends on f(a[i-1]) then you must necessarily recurse, and if you have limited stack space then you must either use a loop or rely on tail-call optimization. Both are valid, but suggesting that "filtering, reduction and transformation" will help is absolutely not true.


If f(a[i]) depends on f(a[i-1]) then you must necessarily recurse,

I think the author already covered this case. It's basically a classic usage of fold:

    fold f a 0 = f(a[i], f(a[i-1], f(..., f(a[0], 0)...)))
He even points out that it's better for the compiler if code is written this way. I.e.:

    a = map g inputData
    b = fold f a 0
The compiler now knows the first line can be parallelized, whereas the second cannot. If the map were mixed up with the fold code (which it often is in big for loops), the compiler would have a harder time of it.


Unsurprising for a Haskeller, but he forgot the fourth and likely most common use of for loops: iterative code that has side effects.

In haskell, this is done as follows:

    sequence_ (map (\x -> putStrLn (show x)) [1..10])
This is equivalent to:

    for i in range(1,11):
        print str(x)
However, in my experience, this is a highly uncommon use of for loops. I've almost never done it, either in haskell or in python. Consider the side effectful code:

    for line in open("urls.txt").readlines():
        scrape_url(line)
Do I really need to do this in order?


I don't think this is the most common--even when you have side-effects, most loops are conceptually going over some collection. Python takes this to an extreme with its for...in loop which is just an ugly map, but it's true in most languages.

Just because you have side-effects does not mean you can't express your computation more neatly using a higher-order function. And some of the imperative uses of a loop are easy to abstract into a higher-order function as well: while (true) {...} becomes forever, for example.


map is a pretty bad example in python due to it being eager while for can be made lazy with yielding instead of returning. Further, list comprehensions and generator expressions tend to be (at least in my experience) fewer characters than map/filter. To get lazy map/filter you need to go out of the core into itertools.


All of that sounds like faults of Python, particularly with its horrible lambda syntax. (E.g. 'map (+ 2) list' is much easier to read than a list comprehension.) Besides, I am not sure why you couldn't generalize map to lazy structures the same way you do with for--Python might not do that, but that is again Python's fault.

Conceptually, a for...in loop is basically like a map, except that it doesn't return a list. Of course, this is yet another fault of Python with an arbitrary separation of statements and expressions. If the for...in loop was just an expression--and that's basically what a list comprehension is, if you squint--it would probably be exactly like map.

My real point was that the only way to get C-style for-loop behavior in Python is to do for i in range(a,b), which is basically equivalent to map fn [1..10].




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: