4

I'm looking for examples of functional code in ruby. Maybe you know some gems, where I can find such a code?

5
  • 2
    Can you please define what you mean by "functional"? There are two very different definitions of that term in wide use: programming with higher-order procedures and programming without side-effects. It is almost impossible to write Ruby without using higher-order procedures, so if you use the first definition, then your answer is that pretty much all Ruby code is functional. OTOH, it is almost impossible to write code without side-effects, so if you use the second definition, then the answer is that almost no Ruby code is functional. Commented Sep 30, 2010 at 18:36
  • @Jörg: Which side-effects does banister's code snippet have? Commented Sep 30, 2010 at 18:51
  • @sepp2k: I have no idea. And that's exactly the problem. I have no idea whether select, %, ==, map, * or take have side-effects or not. But if even one of them has even a single side-effect, then the entire piece of code is not functional according to the second definition. Side-effects are contageous. That's why it is so hard to write side-effect-free code in Ruby: you cannot call any method that you haven't written yourself. Commented Sep 30, 2010 at 19:19
  • @jorg: there are degrees of "functional". even though it doesn't include a monad system, I would say bannisters answer is probably the thing I would be looking for if I asked that question, not a description of why ruby isn't haskell. Commented Oct 1, 2010 at 0:33
  • 1
    @Matt Briggs: I only know of one functional language that includes a monad system out of the box, so I don't see how that's relevant. Commented Oct 1, 2010 at 2:07

6 Answers 6

7

I've been gathering examples of functional programming with Ruby:

Sign up to request clarification or add additional context in comments.

3 Comments

Awesome! Note that just this week there was a proposal on the ruby-core mailinglist for Enumerable#lazy (which is similar to Scala's view method if you are familiar with that), which returns a lazy "view" of the underlying collection and provides lazy implementations of Ruby's Enumerable protocols. So, e.g. calling an_enumerator.lazy.map { ... } will not result in an infinite loop even if the Enumerator is infinite.
@Jörg: thanks for the reference I don't follow the mailing-list (I should...). The Facets project (when you think you have found some new and original abstraction, those guys made it years ago!) use this same "lazy" proxy, they call it "defer": require 'facets'; (1..1.0/0).defer.select { |x| x % 2 }.map { |x| 2*x }.take(5).to_a #=> [2, 4, 6, 8, 10]. It would be very nice that vanilla Ruby go in this direction (IMO operations on enumerators should return enumerators, never arrays), the support for laziness is still poor.
I think that all collection operations should be type-preserving. I.e. operations on a Set should return a Set, an Enumerator should return an Enumerator and so on. The way it is done in Ruby or .NET, where all operations return the same type, regardless of what you started with, is really annoying. At least in .NET, you get a very general, abstract, potentially lazy interface type (IEnumerable), but in Ruby you always get a concrete, eager, fully realized Array. Scala gets this right. (Scala's collections framework is hands down the best. Smalltalk, STL, Ruby aren't even close.)
4

Have you looked at Enumerable?

(1..100).select { |i| i % 5 == 0 }.map { |i| i * 5 }.take(3) #=> [25, 50, 75] 

Comments

4

A couple of months ago, I wondered whether it was possible to make a mixin that behaves exactly like Enumerable, but is based on folding instead of enumerating. Or more precisely, I already knew that it was possible to do this, since fold is a generic iteration operation that is equal in expressive power to foreach, but I wanted to know what it would look like and how hard it would be.

I got bored after I had implemented most methods up to the letter g, so it's incomplete. Also, the version I show here is simplified, since making it behave exactly like Enumerable is a PITA in Ruby. (For example, there are overloaded methods in there, but Ruby doesn't support overloading. This isn't a problem for most Ruby implementations, because they implement Enumerable in Java or C# or other languages that do support overloading, but it is pretty painful when doing it in pure Ruby, so I decided to leave it out.)

Enumerable is full of higher-order procedures, and of course fold (or reduce / inject as it is called in Ruby) is itself a higher-order procedure, so this code is full of them.

module Enumerable def all? reduce(true) {|res, el| res && yield(el) } end def any? reduce(false) {|res, el| res || yield(el) } end def collect reduce([]) {|res, el| res + yield(el) } end alias_method :map, :collect def count reduce(0) {|res, el| res + 1 if yield el } end def detect reduce(nil) {|res, el| if yield el then el end unless res } end alias_method :find, :detect def drop(n=1) reduce([]) {|res, el| res.tap {|res| res + el unless n -= 1 >= 0 }} end def each reduce(nil) {|_, el| yield el } end def each_with_index reduce(-1) {|i, el| (i+1).tap {|i| yield el, i }} end def find_all reduce([]) {|res, el| res.tap {|res| res + el if yield el }} end alias_method :select, :find_all def grep(pattern) reduce([]) {|res, el| res.tap {|res| res + yield(el) if pattern === el }} end def group_by reduce(Hash.new {|hsh, key| hsh[key] = [] }) {|res, el| res.tap {|res| res[yield el] << el }} end def include?(obj) reduce(false) {|res, el| break true if res || el == obj } end def reject reduce([]) {|res, el| res.tap {|res| res + el unless yield el }} end end 

You will notice that I use side-effects in some places. I could have written it without side-effects instead, but it would have been much more complicated. And the problem is that there is no way for me to ensure that the reduce method, which is provided by the client of the mixin, or any of the methods from the core library, don't have side-effects.

Another nice example is the Prelude library for Ruby, which is an implementation of some parts of the Haskell Prelude (Haskell's equivalent to Ruby's core library) in Ruby.

Comments

2

You might be interested in the talk "Better Ruby through Functional Programming" by Dean Wampler, it shows how to use a FP style with Ruby:

http://vimeo.com/6701329

There's also "Thinking functionally in Ruby" by Tom Stuart:

http://skillsmatter.com/podcast/ajax-ria/enumerators

2 Comments

Second link is a purely for-pay ebook link.
The second link is a video presentation about FP in Ruby: "During our last meeting, Tom was surprised by how many people (both Ruby newbies and "Rails not Ruby" programmers) were asking questions about Enumerable, e.g. what map/inject/zip etc do."
1

"The Ruby Programming Language" has half a chapter where they work on a couple of additions to enumerable to make ruby look like Haskell.

You may also want to browse previous Stack Overflow questions tagged with both "Ruby" and "functional-programming" here.

Comments

0

This is not pretty or efficient code. It is to explain the difference between a functional and non functional style.

Something you need to think about when doing functional concepts in ruby is that our vm is not built for it, so without transparent referential optimization, you WILL eat a lot more memory writing in a functional style.

# non functional class Post attr_accessor :title, :body end all_posts = [] # create a post p = Post.new p.title = "Hello world" p.body = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." # put post in the array all_posts << p # fix post title all_posts[0].title = "Opps, fixed title" #functional class Post attr_reader :title, :body def initialize(attrs={}) attrs.each {|k,v| instance_variable_set "@#{k.to_s}", v } end end all_posts = [] # create a post p = Post.new({title: "Hello world", body: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."}) # add to array via new array creation all_posts = all_posts + [p] # be wary when all_posts is a very large collection! old_post = p p = Post.new({title: "Oops, fixed title", body: old_post.body}) all_posts = all_posts - [old_post] + [p] 

I post on functional programming in ruby quite often at my blog: http://rubylove.io

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.