4 ms·
On the flip side, I had to scratch my head on what "&:" is supposed to mean. Or perhaps it's "&" ":". I know, I'll Google it </yeahright>. More seriously, "for
by pacala 14y ago
On the flip side, I had to scratch my head on what "&:" is supposed to mean. Or perhaps it's "&" ":". I know, I'll Google it </yeahright>.
More seriously, "for ..." maps into "map", "foldl", "foldl'", "foldr" and perhaps other constructs. They do pretty much the same thing, but I don't necessarily see a real cognitive advantage of either style.
- epochwolf 14y agoThe language is ruby. array.map(&:to_s) eventually means array.map{|str| str.to_s } As I understand it, the & means convert a variable to a code block. If the object already is a callable object (proc or lambda) ruby doesn't need to do anything to pass it through. Symbols (:to_s) aren't callable so ruby calls the to_proc method on it. The rails framework addd this for Ruby 1.8. The code is below. # File activesupport/lib/active_support/core_ext/symbol.rb, line 10 def to_proc Proc.new { |*args| args.shift.__send__(self, *args) } end
- Falling3 14y agostouset is obviously not arguing that the ruby version with "&:" is immediately obvious to any programmer. The point is if you know ruby, you will understand that line a lot quicker than the second example.
- pacala 14y agoWhat is the "fold" variant in Ruby? Maps are trivial either way, but folds are a pita in my functional programming experience.
- bjeanes 14y ago[1, 2, 3, 4, 5].reduce(0, &:+) #=> 15 Which is short hand for: [1, 2, 3, 4, 5].reduce(0) { |memo, item| memo + item } #=> 15
- dan-k 14y agoWith reduce you don't even need to use the & notation, it has that syntactic sugar built in, so you can just do this: [1, 2, 3, 4, 5].reduce(:+)
- pacala 14y agoYes and no. In practice, you're going to fold into a different domain, which causes the types of the reduce function arguments to be different. Moreover, you are not going to have your reduce function conveniently in a library. You are going to have to write a loop body. At which point, choosing a block over a for loop becomes purely religious.
- krichman 14y agoDifferent domain is fine. [1,2,3].reduce([], &:unshift) #=> [3,2,1] I don't see a problem with writing a for loop, but I think it's not as composable and it can be a little harder to read. So I don't think it's purely religious but it's close to that.
- pacala 14y agoRandomly searching for foldr on code.google.com inserts :: [(VarId,VarId)] -> Env -> Env inserts vvs env = foldr (\(a,b) acc -> M.insert a b acc) env vvs or inserts(vvs, env) { for ((a, b) : vvs) { env = M.insert(a, b, env) } return env } Pick your poison, but I'll stick with fors. Go blues!
- krichman 14y agoIf you want inscrutable Haskell you should go with the <$> operators :) I'd personally still rather have a vvs.reduce(env){|env, (a,b)| M.insert(a, b, env)} but the for loop is perfectly readable also because it fits on a single line. I think for longer bodied reduces I might prefer a for loop actually.
- nightski 14y agoIt's bigger than that. You are separating the how from the what. Sure you could compute a map with a for loop. Or you could compute it in parallel across 1000 threads. Not only that, map is composable. Something a for loop never will be.
- pacala 14y agoFor loops have been parallelized for quite a while. Having a dataflow analysis tell apart a "map" loop from a "fold" loop is quite trivial once you have the framework in place (aliasing not withstanding). Heck, Fortran is all about for loops and it's the language for parallel computations. http://software.intel.com/en-us/articles/automatic-parallelization-with-intel-compilers http://software.intel.com/en-us/articles/automatic-paralleli...
- nightski 14y agoI am aware but it misses the point. Parallel computation is just one example. What about traversing trees? Graphs? Any functor? Any compiler directions to automagically make the for loop traverse those? And that does not even address the issue of composability - which is even more important. Map fusion?
- pacala 14y agoBottom up tree traversal doesn't require an explicit for loop as long as the language supports first class functions. bottomsUp(node) { recurseChildren(node, &bottomsUp); switch (node.id) { case FOO: break; case BAR: break; } } At the end of the day the actual code (the switch) is what matters, and the "glue" can be made a couple of lines either way. PS. On a separate language feature, I like match with algebraic datatypes more than switch, but I don't know if Go has match. Go Rust!