Evan Martin (evan) wrote in evan_tech,
Evan Martin


_why has a hack to generalize _with_index from Ruby, but it's quite complicated.

Background: many Ruby iterators have a _with_index flavor that gives you each value along with its index in the collection. For example, you can do
array.each do { |element| ... }
or you can do
array.each_with_index do { |element, index| ... }
but it's always felt a bit lacking that you have to implement both versions when you're writing code. (For example, I've needed map_with_index in a few programs so I've implemented it myself.)

Whenever I see these sorts of complicated hacks, I think to myself, "transforming code? that's functional!" The Ruby code there does crazy stuff like creating method names from strings and sticking instances variables into a block. Here's a more sane implementation in Haskell.

So, for example, I can take code like:
map (\char -> toUpper char) ['a', 'b', 'c']
and when I need the index of each element it becomes
map (\(char,index) -> some_code_using_char_and_index) (with_index ['a', 'b', 'c'])

The magic "with_index" code is simple enough that it's almost not worth including:
with_index :: [a] -> [(a, Int)]
with_index l = zip l [0..]

Laziness makes many "iterator" patterns nearly transparent in Haskell, because you don't have this tension between "do I create a data structure and return the whole thing?" or "do I expose an interface to get each element sequentially?" as they're both implemented the same way. (It's not completely magical, of course; sometimes IO complicates things.)

(Rubyists will probably feel my pain when they see the following snippet. I feel like I've written code like this a million times, where "foo", "bar", and "baz" depend on the situation:
def foo
  ret = []
  bar.baz { |x| ret << x }
Tags: haskell, ruby
  • Post a new comment


    default userpic
    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.