Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Haskell style list comprehensions in Ruby (gist.github.com)
87 points by ldubinets on May 12, 2013 | hide | past | favorite | 28 comments


Somewhat related: I've developed a library for operations involving multi-dimensional arrays [1, 2]. When possible it uses GCC for jit-compilation to achieve higher performance.

  require 'multiarray'
  include Hornetseye
  # Object
  lazy(4) { |i| i + 2 }
  # Sequence(INT):
  # [ 2, 3, 4, 5 ]
  lazy(3, 2) { |x, y| x }
  # MultiArray(INT,2):
  # [ [ 0, 1, 2 ],
  #   [ 0, 1, 2 ] ]
  lazy(3, 2) { |x, y| x + 1 }
  # MultiArray(INT,2):
  # [ [ 1, 2, 3 ],
  #   [ 1, 2, 3 ] ]
  lazy(3, 3) { |x, y| y + 4 }
  # MultiArray(INT,2):
  # [ [ 4, 4, 4 ],
  #   [ 5, 5, 5 ],
  #   [ 6, 6, 6 ] ]
  lazy(3, 3) { |x, y| (x + 1) * (y + 4) }
  # MultiArray(INT,2):
  # [ [ 4, 8, 12 ],
  #   [ 5, 10, 15 ],
  #   [ 6, 12, 18 ] ]
  lazy { |x,y| Sequence['n', 'p', 'r', 't'][x] + Sequence['a', 'i', 'u', 'e', 'o'][y] }
  # MultiArray(OBJECT,2):
  # [ [ "na", "pa", "ra", "ta" ],
  #   [ "ni", "pi", "ri", "ti" ],
  #   [ "nu", "pu", "ru", "tu" ],
  #   [ "ne", "pe", "re", "te" ],
  #   [ "no", "po", "ro", "to" ] ]
  s = lazy(33) { |i| 3 * (i+1) }
  # Sequence(INT):
  # [ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, .... ]
  s.mask((s % 2).eq(0)).collect { |i| i ** 2 / 3 }
  # Sequence(INT):
  # [ 12, 48, 108, 192, 300, 432, 588, 768, 972, 1200, 1452, 1728, .... ]
[1] https://github.com/wedesoft/multiarray [2] http://www.wedesoft.de/hornetseye-api/


It's neat that Ruby has programmable syntax like this, but Haskell's comprehensions are one of its worst syntactic features IMHO. They're inherently non-composable.


They're equivalent to do-notation in the List monad so as long as you don't really need the inline syntax you can just mechanically translate it to do nation and back.

And with -XMonadComprehensions you can have the compiler do that for a whole bunch more Monads.


It's also possible to just use Applicative with <*> and <$>


Cue to Haskell people lamenting the fact that this is an abomination and not by any means "Haskell-style list comprehensions".

Still very nice though.


Haskell person here... I personally find Haskell list comprehensions to be ridiculously ugly, and are an unnecessary bit of syntactic sugar over the List Monad.


... you might want to read up on monad comprehensions :)

http://hackage.haskell.org/trac/ghc/wiki/MonadComprehensions


Okay, as much as I hate syntactic sugar... that's pretty freakin awesome.


That's all well and good, but why not just use do notation?


I find this interesting. As someone who emphatically doesn't know Haskell, but has recently started learning, I think list comprehensions are wonderful. That said, I haven't got to the chapter on Monads yet, so... :)

What do you find ugly about them?


List comprehensions as a programming technique are incredibly useful, don't get me wrong. I am just not a fan of syntactic sugar in general.

EDIT: On a more elaborate note, I think that the extreme variety of programming styles enabled by Haskell is more of a weakness than a strength. True, defining your own abstractions and operators is part of the fun, but seeing declarative style (pattern-matching in function defs, where blocks, function args on left-hand side, guards), expression style (let blocks, lambda abstractions, case statements, if clauses), applicative style (<$>, <* >, <$, <* , * >, <|>), and arrow style (* * * , &&&, ^>>, ^<<, >>^, <<^, <+>, +++, |||, >>>, <<<) all in the same file can get pretty overwhelming even if you're already pretty fluent in the language.

We don't need any help being confusing, people. We already have Monads, for god's sake.


"A monads is just a monoid in the category of endofunctors, what's the problem?"


This has no business being at the top of HN.


Why not? Seems pretty damn cool to me.

Edit: didn't realise you were the author :)


As its creator, I would certainly agree. Still, I'd feel bad if some poor soul considered it anything but an interesting demonstration of Ruby's capabilities.


Btw, it would be great if you add some comments there. What are the key (all of it?) parts? etc...


It's awesomely cool, but it also defines a global method_missing, which puts you in for a world of pain before even considering the overloaded operators.

It's one of those hacks that are cute and great to play with and awesome proof of concept for new features, but if I saw it in a production codebase, I'd want to strangle someone.


Not everything has to be related with entrepreneurship. It is a hacker news after all...

EDIT: just realized you're the author, so your comment must be out of modesty. Anyway, it deserves, and kudos to you ;)


Oh wow, that is so wrong and so beautiful.


As someone who is still learning Ruby and would like to learn Haskell I have no idea what is going on here. Would someone be kind enough to explain? Thanks.


Specifically, it is monkey patching the unary - and + operators on array, and has a clever global method missing.

The unary operators `def -@` gets called when you do something like: `-[1,2,3]`.

So in the example code at the bottom, it's doing `bar =+ [x | x <- [1..3]]` that's better parsed as `bar = +[x | x <- [1..3]]` (note the spacing difference).

Hacking unary operators has a long history in silliness in ruby. See also: https://github.com/jicksta/superators


It's just adding a syntactic construct to ruby which looks similar to haskells list comprehensions. Explanation for haskell: http://learnyouahaskell.com/starting-out#im-a-list-comprehen...


Ah, I get it. Thanks dude.


Side question, rant. I love the syntactic side of list or dict comprehension (in python) and often use them but as soon as I have to debug or expand the functionality, I have to slice them into for loop. And then I hate myself for being lazy/clever and more and more, when I start typing a = [, I hear an internal voice: wait, aren't you being wrongfully clever one again?

I am the only one?


I adore list comprehensions, but perhaps for this reason, I generally only use them for operations so simple as to be trivially correct or incorrect without any debugging.

Dict comprehensions are just a little muddier, even for simple things, but a single line of debug output after the comprehension should confirm whether or not you're getting the structure you expect, provided you are still doing only trivial work inside your comprehension.


I like to use them for simple cases only (No nested comprehensions, etc). When in doubt, the Google Python style guide [0] has some useful guidance.

[0] - http://google-styleguide.googlecode.com/svn/trunk/pyguide.ht...


Wow, that's some nontrivial Ruby, some explanation of this code would be nice, I program in Ruby for some 6 years now and I still had to do a lot of head-scratching to more or less figure this out. I had no idea Ruby allows overloading of prefix operators, for example.


There is an excellent book called "Metaprogramming Ruby" I'd recommend.




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

Search: